Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a type excluding instance methods from a class in typescript?

Given a class, containing both properties and methods, I'd like to derive a type that just contains its properties.

For example, if I define a class as follow:

class MyObject {

  constructor(public prop1: string, public prop2: number) {}

  instanceMethod() { ... }
}

I'd like to have a type, say MyObjectConstructor that would be like this:

type MyObjectConstructor = {
  prop1: string;
  prop2: number;
}

I know I can use the built-in type Pick and manually select the keys I want by name, but I don't want to have to repeat the keys all over, and have to change them every time I add new properties to my class.

Is there a way to define a generic type ConstructorType<T>that only returns the properties of a class in typescript ?

like image 222
HHK Avatar asked Apr 02 '19 16:04

HHK


People also ask

Can I use a class as a type in TypeScript?

TypeScript treats a class as both value and type. This implicit type declared by TypeScript describes the shape of the instance a class produces. Therefore when a class is used as a type, such as using let value :Class annotation, TypeScript checks if the value has all the public properties of the Class .

How do I create an instance of a class in TypeScript?

In the highlighted code, you added a parameter called name of type string to your class constructor. Then, when creating a new instance of the Person class, you are also setting the value of that parameter, in this case to the string "Jane" . Finally, you changed the console. log to print the argument to the screen.

How do you omit in TypeScript?

The TypeScript Omit utility type It will remove the fields you defined. We want to remove the id field from our user object when we want to create a user. type UserPost = Omit<User, 'id'>; const updateUser: UserPost = { firstname: 'Chris', lastname: 'Bongers', age: 32, };

How do you define a method of a class in TypeScript?

In TypeScript, the constructor method is always defined with the name "constructor". In the above example, the Employee class includes a constructor with the parameters empcode and name . In the constructor, members of the class can be accessed using this keyword e.g. this. empCode or this.name .


2 Answers

I've found a way to exclude all properties that match a given type, thanks to this article: https://medium.com/dailyjs/typescript-create-a-condition-based-subset-types-9d902cea5b8c

I made a few adaptations, but here is the details:

// 1 Transform the type to flag all the undesired keys as 'never'
type FlagExcludedType<Base, Type> = { [Key in keyof Base]: Base[Key] extends Type ? never : Key };
    
// 2 Get the keys that are not flagged as 'never'
type AllowedNames<Base, Type> = FlagExcludedType<Base, Type>[keyof Base];
    
// 3 Use this with a simple Pick to get the right interface, excluding the undesired type
type OmitType<Base, Type> = Pick<Base, AllowedNames<Base, Type>>;
    
// 4 Exclude the Function type to only get properties
type ConstructorType<T> = OmitType<T, Function>;

Try It

There might be a simpler way, I've tried playing with ConstructorParameters and defining a constructor signature but without results.

Update

Found an equivalent while browsing the typescript documentation here: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types

type NonFunctionPropertyNames<T> = {
  [K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;

It's a bit less verbose since the omitted type is not generic, but it's the same idea.

like image 158
HHK Avatar answered Nov 15 '22 21:11

HHK


Given a class, containing both properties and methods, I'd like to derive a type that just contains its properties.

From your example, it seems like you want the result to contain only fields (as opposed to only properties). Here is a type that picks out the fields from an object or class instance.

type DataPropertyNames<T> = {
  [K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];

type DataPropertiesOnly<T> = {
  [P in DataPropertyNames<T>]: T[P] extends object ? DTO<T[P]> : T[P]
};

export type DTO<T> = DataPropertiesOnly<T>;

I have used the acronym DTO to mean Data Transfer Object. Thank you to l00ser2410656 for this playground demo.

like image 44
Shaun Luttin Avatar answered Nov 15 '22 21:11

Shaun Luttin