TypeScript:创建类型,根据值类型删除 interface/class 的属性
TypeScript: Create type that removes properties of an interface/class based on their value-types
想象一下 class:
class Person {
name = "Ann";
sleep() { /*...*/ }
eat() { /*...*/ }
}
现在我想创建一个只提取方法的类型:
// should work:
const personMethods: MethodsOf<Person> = { sleep() {}, eat() {} };
const names: keyof MethodsOf<Person>[] = ['sleep', 'eat'];
// should fail:
const personMethods: MethodsOf<Person> = { notAPersonMethod() {} };
const names: keyof MethodsOf<Person>[] = ['not', 'existing', 'methods'];
我在打字稿文档中找不到任何内容,到目前为止我的尝试都失败了。我试过了:
type MethodKeys<
Type extends object,
Key extends keyof Type,
> = keyof Record<Type[Key] extends AnyFn ? Key : never, any>;
type Methods<Type extends object> = {
[Property in MethodKeys<Type, keyof Type>]: Type[Property];
};
const test: MethodKeys<Person> = 'name'; // works, but shouldn't :(
const test2: Partial<Methods<Person>> = { name: 'Ann' }, // works too :/
感谢您的帮助!
您所需要的只是一个类型,它可以返回给定 class/interface 的方法名称。您可以使用 mapped type 创建一个对象类型,其中每个值代表每个方法名称。然后,您可以索引该对象类型以获取其值类型 - 这将产生所有方法名称的联合。
type MethodKeys<T> = {
[K in keyof T]: T[K] extends (...x: any) => any ? K : never;
}[keyof T]
现在,您可以使用 Pick
从 class/interface 中选择那些方法键。
type MethodsOf<T> = Pick<T, MethodKeys<T>>;
并且您给定的测试用例工作正常-
// works
const personMethods: MethodsOf<Person> = { sleep() {}, eat() {} };
const names: Array<keyof MethodsOf<Person>> = ['sleep', 'eat'];
const personMethods1: MethodsOf<Person> = { notAPersonMethod() {} };
// ^ Type '{ notAPersonMethod(): void; }' is not assignable to type 'MethodsOf<Person>'.
const names1: Array<keyof MethodsOf<Person>> = ['not', 'existing', 'methods'];
// ^ Type '"not"' is not assignable to type 'MethodKeys<Person>'.
// ...and more
在 playground 上查看。
除了@Chase 的解决方案之外,您还可以使用distributive conditional types 来过滤掉具有函数值的键:
type MethodHelper<T, K extends keyof T> = K extends any
? T[K] extends Function ? K
: never : never;
然后用它来实现 MethodKeys
和 Methods
:
type MethodKeys<T> = MethodHelper<T, keyof T>;
type Methods<T> = {
[K in MethodKeys<T>]: T[K]
};
const test: MethodKeys<Person> = 'name'; // doesn't work
const test2: MethodKeys<Person> = 'sleep'; // does work
const test3: Partial<Methods<Person>> = { name: 'Ann' }; // doesn't work
const test4: Partial<Methods<Person>> = { sleep() {} }; // does work
想象一下 class:
class Person {
name = "Ann";
sleep() { /*...*/ }
eat() { /*...*/ }
}
现在我想创建一个只提取方法的类型:
// should work:
const personMethods: MethodsOf<Person> = { sleep() {}, eat() {} };
const names: keyof MethodsOf<Person>[] = ['sleep', 'eat'];
// should fail:
const personMethods: MethodsOf<Person> = { notAPersonMethod() {} };
const names: keyof MethodsOf<Person>[] = ['not', 'existing', 'methods'];
我在打字稿文档中找不到任何内容,到目前为止我的尝试都失败了。我试过了:
type MethodKeys<
Type extends object,
Key extends keyof Type,
> = keyof Record<Type[Key] extends AnyFn ? Key : never, any>;
type Methods<Type extends object> = {
[Property in MethodKeys<Type, keyof Type>]: Type[Property];
};
const test: MethodKeys<Person> = 'name'; // works, but shouldn't :(
const test2: Partial<Methods<Person>> = { name: 'Ann' }, // works too :/
感谢您的帮助!
您所需要的只是一个类型,它可以返回给定 class/interface 的方法名称。您可以使用 mapped type 创建一个对象类型,其中每个值代表每个方法名称。然后,您可以索引该对象类型以获取其值类型 - 这将产生所有方法名称的联合。
type MethodKeys<T> = {
[K in keyof T]: T[K] extends (...x: any) => any ? K : never;
}[keyof T]
现在,您可以使用 Pick
从 class/interface 中选择那些方法键。
type MethodsOf<T> = Pick<T, MethodKeys<T>>;
并且您给定的测试用例工作正常-
// works
const personMethods: MethodsOf<Person> = { sleep() {}, eat() {} };
const names: Array<keyof MethodsOf<Person>> = ['sleep', 'eat'];
const personMethods1: MethodsOf<Person> = { notAPersonMethod() {} };
// ^ Type '{ notAPersonMethod(): void; }' is not assignable to type 'MethodsOf<Person>'.
const names1: Array<keyof MethodsOf<Person>> = ['not', 'existing', 'methods'];
// ^ Type '"not"' is not assignable to type 'MethodKeys<Person>'.
// ...and more
在 playground 上查看。
除了@Chase 的解决方案之外,您还可以使用distributive conditional types 来过滤掉具有函数值的键:
type MethodHelper<T, K extends keyof T> = K extends any
? T[K] extends Function ? K
: never : never;
然后用它来实现 MethodKeys
和 Methods
:
type MethodKeys<T> = MethodHelper<T, keyof T>;
type Methods<T> = {
[K in MethodKeys<T>]: T[K]
};
const test: MethodKeys<Person> = 'name'; // doesn't work
const test2: MethodKeys<Person> = 'sleep'; // does work
const test3: Partial<Methods<Person>> = { name: 'Ann' }; // doesn't work
const test4: Partial<Methods<Person>> = { sleep() {} }; // does work