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
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 unknown
instead.
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