admin管理员组

文章数量:1122832

I am working on a project and have a need to do some custom schema work. I have a type of MyModel and am trying to extract all properties that inherit from SchemaIdentity<any> and make those properties into a union using ExtractIdentity<>.

I have ExtractIdentity<MyModel>, but it is extracting all properties (id, name), not the ones that inherit from SchemaIdentity. The result should be id What am I doing wrong?

I have tried changing [K in keyof R]: R[K] extends SchemaIdentity<any> ? K : never; and it seems like narrowing is not working as expected. I would expect any R[K] that inherits from SchemaIdentity to return is property name of K. The name property on MyModel has a type of SchemaDefault<string> yet it's some how matching the type narrowing.

NOTE: I have omitted code in the classes to keep the example smaller.


export abstract class SchemaBase<T extends any>  {

}

export class SchemaDefault<T extends any> extends SchemaBase<T>  {


}

export class SchemaNumber<T extends number = number> extends SchemaBase<T> {

}

export class SchemaIdentity<T extends string | number> extends SchemaBase<T> {

}

export class SchemaDefinition<T extends {}> extends SchemaBase<T> {

}

export type ExtractIdentity<T> = T extends SchemaDefinition<infer R> ? {
    [K in keyof R]: R[K] extends SchemaIdentity<any> ? K : never;
}[keyof R] : never;

type MyModel = SchemaDefinition<{
    id: SchemaIdentity<number>;
    name: SchemaDefault<string>;
}>


type MyIds = ExtractIdentity<MyModel>

Playground Here

TypeScript Version: 5.6.3

I am working on a project and have a need to do some custom schema work. I have a type of MyModel and am trying to extract all properties that inherit from SchemaIdentity<any> and make those properties into a union using ExtractIdentity<>.

I have ExtractIdentity<MyModel>, but it is extracting all properties (id, name), not the ones that inherit from SchemaIdentity. The result should be id What am I doing wrong?

I have tried changing [K in keyof R]: R[K] extends SchemaIdentity<any> ? K : never; and it seems like narrowing is not working as expected. I would expect any R[K] that inherits from SchemaIdentity to return is property name of K. The name property on MyModel has a type of SchemaDefault<string> yet it's some how matching the type narrowing.

NOTE: I have omitted code in the classes to keep the example smaller.


export abstract class SchemaBase<T extends any>  {

}

export class SchemaDefault<T extends any> extends SchemaBase<T>  {


}

export class SchemaNumber<T extends number = number> extends SchemaBase<T> {

}

export class SchemaIdentity<T extends string | number> extends SchemaBase<T> {

}

export class SchemaDefinition<T extends {}> extends SchemaBase<T> {

}

export type ExtractIdentity<T> = T extends SchemaDefinition<infer R> ? {
    [K in keyof R]: R[K] extends SchemaIdentity<any> ? K : never;
}[keyof R] : never;

type MyModel = SchemaDefinition<{
    id: SchemaIdentity<number>;
    name: SchemaDefault<string>;
}>


type MyIds = ExtractIdentity<MyModel>

Playground Here

TypeScript Version: 5.6.3

Share Improve this question edited Nov 22, 2024 at 16:25 Agrejus asked Nov 22, 2024 at 16:01 AgrejusAgrejus 7628 silver badges21 bronze badges 4
  • It is i beleave because.SchemaDefault and ExtractIdentity matches look here if that answers your question playground – zedryas Commented Nov 22, 2024 at 16:23
  • That is interesting @zedryas, so even if the types (Classes) are named differently, if they match because technically all the internal properties/methods are the same? – Agrejus Commented Nov 22, 2024 at 16:28
  • Your answer does work if you want to post it @zedryas – Agrejus Commented Nov 22, 2024 at 16:36
  • "even if the types (Classes) are named differently, if they match because technically all the internal properties/methods are the same?" yes, you can trivially test this class Foo {}; class Bar {}; const foo: Foo = new Bar();. Also note that this check R[K] extends SchemaIdentity<any> will always succeed as the answer by zedryas shows. The word "extends" in this context means "is assignable to", and everything is assignable to any. – Jared Smith Commented Nov 22, 2024 at 17:10
Add a comment  | 

1 Answer 1

Reset to default 2

The issue you do have is that all types matches even when you use SchemaNumber which is slightly diferent but reduce to the same type

If you try this:

type test1 = SchemaNumber<any> extends SchemaIdentity<any> ? true : false
//         ^? false       
type test2 = SchemaIdentity<any> extends SchemaNumber<any> ? true : false
//         ^? true

What you can ses is that SchemaIdentity extends even SchemaNumber and further more SchemaDefault which takes any as a generic

On that matter try to avoid any and you can alway avoid them use unknowninstead.

So for it to work you need to have differents types where extends does not match.

If SchemaIdentity would take a string and only a string then it would work agains SchemaNumber but still not agains SchemaDefault.

Another way to make it work is to have different type - in this case have diffent properties / methods etc.

So if you want to keep the generic types simple code your class and it will work - except if all properties match as well.

I hope it helps

本文标签: TypeScriptType Narrowing In Generics Not WorkingStack Overflow