HHK
HHK

Reputation: 1917

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 ?

Upvotes: 43

Views: 18266

Answers (6)

Use this for better perfs :

export type TrueObject = object & {
  [Symbol.iterator]?: never;
  //@ts-expect-error - 'SymbolConstructor' does not exist on type 'object'
  [SymbolConstructor]?: never;
};

Upvotes: 0

fardolieri
fardolieri

Reputation: 621

Since Typescript Version 4.1 you can do:

type ExcludeMethods<T> = 
  { [K in keyof T as (T[K] extends Function ? never : K)]: T[K] }

Upvotes: 19

Rodrigo Alcorta
Rodrigo Alcorta

Reputation: 571

export type Defined<T> = Exclude<T, undefined>;
        
export type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];
    
export type PartialModel<T extends Model> = {
  [K in NonFunctionPropertyNames<T>]?: Defined<T[K]>
};

Upvotes: 0

wangkm
wangkm

Reputation: 39

export type DTO<T> = {
    [P in {
        [K in keyof T]: undefined extends T[K] ? never : T[K] extends (...args) => any ? never : K;
    }[keyof T]]: T[P] extends object ? DTO<T[P]> : T[P];
} &
    Partial<
        {
            [P in {
                [K in keyof T]: undefined extends T[K] ? K : never;
            }[keyof T]]: T[P] extends object ? DTO<T[P]> : T[P];
        }
    >;

Upvotes: 3

HHK
HHK

Reputation: 1917

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.

Upvotes: 33

Shaun Luttin
Shaun Luttin

Reputation: 141662

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.

Upvotes: 5

Related Questions