I have this:
public getFriends() : IUser[] {
let friends: IUser[];
friends[0].Id = "test";
friends[0].Email = "asdasd";
return friends;
}
Could sound stupid, but why am I getting friends[0] undefined? What am I supposed to do if I don't have a class of type User in this case.
In this statement:
let friends: IUser[];
You are declaring friends and its type IUser[], you aren't initializing it yet, so friends's value is undefined at this point.
So, the first step is to initialize it:
let friends: IUser[] = [];
Now it is an array, you have to push some content into it. First you have to create a IUser:
// this is one (likely) way of creating a IUser instance, there may be others
let friend: IUser = {Id: "test", Email: "asdasd"};
And then add it to the array:
friends.push(friend);
So your final code would be:
public getFriends() : IUser[] {
let friends: IUser[] = [];
let friend: IUser = {Id: "test", Email: "asdasd"};
friends.push(friend);
return friends;
}
Related
I want an array that allow only for the value of 'john' and 'james', both are not required, I tried below interface:
interface User {
name: ['john' | 'james']
}
but it doesn't work for
const user: User = ['john', 'james']
it work only
const user: User = ['john']
const user: User = ['james']
I think I missed something
You have typed an array with exactly one element that can either be john or james. If you want to allow multiple elements, you will need to do:
interface User {
name: ('john' | 'james')[]
}
Now all the following are valid:
const u1: User = {
name: ['john']
};
const u2: User = {
name: ['james']
};
const u3: User = {
name: ['john', 'james']
};
If the use of an interface is not mandatory, you can take advantage of union types:
type User = 'john' | 'james';
const user1:User[] = ['john', 'james', 'jane'];
// ----------------------------------> ~~~~~~
// Type '"jane"' is not assignable to type 'User'.(2322)
const user2:User[] = ['john', 'james', 'james']; // No errors
I have a function called invoke which currently is typed like this:
export type InvokeType = <T = InvokeResponseTypes, Params = any>(
methodName: InvokeMethodNameTypes,
args?: Params
) => Promise<T>;
// for context, Invoke is a signalR method to call methods on a SignalR server.
InvokeMethodNameTypes are simply strings that call methods, like "GetUser" or "GetEvent" etc.
Each InvokeMethodNameType has corresponding args (Params) or none at all and I would use it like so:
const data = await invoke<GetEventResponseType, GetEventArgs>("GetEvent", { eventId })
This works but I do not feel like it is the correct approach to what I actually want to achieve.
Is it possible to type the invoke function so when passing a string such as "GetEvent" the arguments are automatically inferred to the correct object type?
Based on the code in the sandbox:
function myFunction(stringArg: 'person'): { name: string, age: number }
function myFunction(stringArg: 'car'): { make: string, model: string }
function myFunction(stringArg: any): any
{
if (stringArg === "person") {
return {
name: "John",
age: 20
};
}
if (stringArg === "car") {
return {
make: "Mazda",
model: "6"
};
}
}
let personResult = myFunction("person");
let personName = personResult.name; // Okay
let personAge = personResult.age; // Okay
let personMake = personResult.make; // Error
let personModel = personResult.model; // Error
let carResult = myFunction("car");
let carName = carResult.name; // Error
let carAge = carResult.age; // Error
let carMake = carResult.make; // Okay
let carModel = carResult.model; // Okay
Playground
I would like to sync an object that is provided by an API to a table.
The table is defined in Sequelize and has an interface and a class:
declare interface SampleInterface {
value1?: string;
value2?: string;
value3?: number;
}
class SampleClass implements SampleInterface {
value1?: string;
value2?: string;
value3?: number;
}
The response of the API is not always the same, but could look something like this:
const sampleResponse = {
value2: "..."
value3: 0
value4: "..."
}
Now, I would only like create a new object that can be passed to sequelize that has the matching contents, for example:
const filteredResponse = {
value2: "..."
value3: 0
}
How can I match the object properties keys to the interface or class?
Thanks!
If I understand you right, you:
Have: API which produces not 100% predictable response.
Want: to create a concrete instance of the class from this untrusted source
If I am right you have two options:
If input object is not very big and dynamic you could do everything explicitly:
const unreliableObject = fetchFromApi();
const result = new Result();
if (typeof unreliableObject.name === 'string') {
result.name = unreliableObject.name;
}
This code is more-less OK except it is toooo verbose.
As a bit more advanced solution, you can create TransformationMapper, something like this:
class MyClass {
name: string;
}
const expectedKeys: (keyof MyClass)[] = ['name'];
const data: any = { v: 1, name: '13212' };
const res = expectedKeys.reduce((result, fieldName) => {
const value = data[fieldName];
if (value != null) {
result[fieldName] = data[fieldName]
}
return result;
}, new MyClass());
console.log(res);
UPDATE
Is there any way, I could the get keyof MyClass programmatically
The main idea is to get a scheme to parse the original response. Luckily, you already got it.
So you need to: create an instance of the desired class, and get keys from it:
This can be done using:
Object.keys()
Object.entries()
const data: any = {};
let result = new MyClass();
result = Object.keys(result).reduce((result, fieldName) => {
const value = data[fieldName];
if (value != null) {
result[fieldName] = data[fieldName]
}
return result;
}, result)
But I also have to warn you. If you don't trust API, you should not only parse, but also validate values you are parsing. In another case, incorrect types provided via API may break your app.
You can write your own validation (it is not that hard) or take something existing like yup
I think you need to extend your TypeScript with custom transformer like this one: ts-transformer-keys to be able to get the keys of an interface and the filter your response by copying only those keys.
Plain TypeScript doesn't generally allow pure type information to be passed to runtime code. So you can't see the interface at runtime and you can't know what are its fields to filter them.
You could get property names of a class, but you have to set them in your code:
class SampleClass implements SampleInterface {
value1?: string = "";
value2?: string = "";
value3?: number = 0;
}
var a = new SampleClass();
for (var k in a) {
console.log(k); // prints value1, value2, value3
}
Alternatively you could declare interface in this weird roundabout way through enum because enum is one of the TypeScript construct that are accessible at runtime:
enum Props {
value1 = "value1",
value2 = "value2",
value3 = "value3"
};
declare interface SampleInterface {
[Props.value1]?: string;
[Props.value2]?: string;
[Props.value3]?: number;
}
class SampleClass implements SampleInterface {
value1?: string = "";
value2?: string = "";
value3?: number = 0;
}
for (var k in Props) {
console.log(k); // prints value1, value2, value3
}
You can get fields of class programmatically if you set default values to them and instantiate the class
class SampleClass {
value1?= "";
value2?= "";
value3?= 0;
}
var keys = Object.keys(new SampleClass()) as (keyof SampleClass)[];
var ob = { value1: "asd", value2: "sdad", value4: "xxx" };
var result: SampleClass = {};
for (var k of keys) {
if (k in ob) { // remove this line if you want set missing fields to undefined
result[k] = (ob as any)[k];
}
}
console.log(result);
I'm really new to javascript.
I have a nested class structure where I need to initiate using a json object. My question is how can I initiate array of EventDate objects and assign to this.dates in CustomerEvents constructor
export default class CustomerEvent {
constructor(customer_event: any) {
this.event_title = customer_event.event_title;
this.customer = customer_event.customer;
this.total_budget = customer_event.total_budget;
this.no_of_people = customer_event.no_of_people;
this.dates = /**array of new EventDate(customer_event.dates) **/;
}
event_title: string;
customer: Customer;
total_budget: number;
no_of_people: number;
dates: EventDate[];
}
class EventDate {
constructor(date: any) {
this.start_date = date.start_date;
this.end_date = date.end_date;
}
start_date: Date;
end_date: Date;
}
If someone could help me on this, it'll be really helpful. Thanks
Just assign new empty array, like this:
constructor(customer_event: any) {
this.event_title = customer_event.event_title;
this.customer = customer_event.customer;
this.total_budget = customer_event.total_budget;
this.no_of_people = customer_event.no_of_people;
this.dates = [];
}
If you need to cast incoming array, you can do this:
...
this.dates = customer_event.dates.map(date => new EventDate(date));
...
Angular Style Guide recommends using interfaces for data model instead of classes:
Consider using an interface for data models.
That being said, you can refactor your code like this:
export interface EventDate {
start_date: Date;
end_date: Date;
}
export interface CustomerEvent {
event_title: string;
customer: Customer;
total_budget: number;
no_of_people: number;
dates: EventDate[];
}
Now when it comes to initialization, you can do it something like this:
const customerEvent: CustomerEvent = {
event_title: 'Some Title',
customer: { /*An Object representing a Customer Type*/ }
total_budget: 123,
no_of_people: 456,
dates: [{
start_date: new Date(),
end_date: new Date()
}]
};
Create those instances yourself:
constructor(customer_event: any) {
this.event_title = customer_event.event_title;
this.customer = customer_event.customer;
this.total_budget = customer_event.total_budget;
this.no_of_people = customer_event.no_of_people;
this.dates = customer_event.dates.map(date => new EventDate(date));
}
I have a a UserModel as shown below.
export class UserModel {
constructor(
public id: string,
public firstName: string,
public lastName: string
) { }
}
If I want an array of Users that complies to UserModel, do I use
var userArray: UserModel[] = []
or
var userArray: [UserModel] = [];
What is the difference?
Looking at the TypeScript handbook we can see that an array is declared as either
let userArray: UserModel[] = [];
or
let userArray: Array<UserModel> = [];
And that a Tuple is declared as
let x: [string, number];
As such, your second statement does not declare an array at all.