I’m trying to create a custom TypeScript type that only accepts field names whose types are related to a specific base class, WhateverBaseClass
.
How can I modify the MyType
definition to ensure it only includes the keys of MyClass
that correspond to fields of type WhateverBaseClass
or arrays of WhateverBaseClass
?
Here’s a simplified version of my code:
class WhateverBaseClass {}
class Invalid {
public invalidName!: string;
}
class Valid extends WhateverBaseClass {
public validName!: string;
}
class MyClass {
public name!: string; // Invalid: string
public validArray!: Valid[]; // Valid: array of Valid
public validField!: Valid; // Valid: instance of Valid
public invalidField!: Invalid[]; // Invalid: array of Invalid
}
type MyType<T> = {
[K in keyof T]: T[K] extends WhateverBaseClass | (WhateverBaseClass[]) ? T[K] : never;
}[keyof T];
// Expectation:
const expectation: MyType<MyClass>[] = ["validArray", "validField"]; // Must only accept these two
// Result:
const rs: MyType<MyClass>[] = ["validArray", "validField", "name", "invalidField"]; // But it accepts all
2
Answers
If you want an array of the keys your code needs to be corrected a bit:
Inside the code I replaced
T[K]
byK
Then it looks like the code is not working but it’s because you didn’t put anything in WhateverBaseClass, so typescript seems to consider that any class extends it.
The code starts to work as expected as soon as I put some properties in the base class.
Working playground here
Your question could translate to:
"I want a type that represents the union of all property name in a type for which the property type matches a constraint"
The constraint here seems to be extends Valid | Valid[].
First we can pick by type:
It will create a new type that only accepts the properties you have targeted by their type.
PickByType<{n: number, s: string}, number> => {n: number}
Since in your case you only want the keys you can write MyType like that:
playground