TypeScript:创建类型,根据其值类型删除接口/类的属性

Posted

技术标签:

【中文标题】TypeScript:创建类型,根据其值类型删除接口/类的属性【英文标题】:TypeScript: Create type that removes properties of an interface/class based on their value-types 【发布时间】:2021-12-20 05:27:15 【问题描述】:

想象一下班级:

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'];

我在 typescript 文档中找不到任何内容,到目前为止我的尝试都失败了。我试过了:

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 :/

感谢您的帮助!

【问题讨论】:

我想你的意思是Array&lt;keyof MethodsOf&lt;Person&gt;&gt;(keyof MethodsOf&lt;Person&gt;)[] 那里。注意优先级,keyof MethodsOf&lt;Person&gt;[] 在数组上应用keyof 【参考方案1】:

您所需要的只是一种类型,它可以返回给定类/接口的方法名称。您可以使用mapped type 创建一个对象类型,其中每个值代表每个方法名称。然后,您可以索引该对象类型以获取其值类型 - 这将产生所有方法名称的联合。

type MethodKeys<T> = 
    [K in keyof T]: T[K] extends (...x: any) => any ? K : never;
[keyof T]

现在,您可以使用 Pick 从您的类/接口中选择这些方法键。

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。

【讨论】:

【参考方案2】:

除了@Chase 的解决方案,您还可以使用distributive conditional types 来过滤掉具有函数值的键:

type MethodHelper<T, K extends keyof T> = K extends any
    ? T[K] extends Function ? K
    : never : never;

然后用它来实现MethodKeysMethods

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

Playground link

【讨论】:

以上是关于TypeScript:创建类型,根据其值类型删除接口/类的属性的主要内容,如果未能解决你的问题,请参考以下文章

根据泛型类型中其值的类型来约束 Key

TypeScript:删除索引签名而不隐式出现“任何”类型错误

TypeScript:你能根据函数的参数定义一个返回类型结构吗?

Typescript 枚举作为参数类型允许无效值

TypeScript系列教程16TypeScript 联合类型

如何将数据组表示为通用 Typescript 接口