打字稿如何使映射类型扩展 typeof Class

Typescript how to make a mapped type extends typeof Class

Typescript 映射类型语法对我来说真的很晦涩,但我会尽量让自己清楚。

基本上我想要一个类型过滤器来 return 我只有一个对象的键具有 Class 类型或 classes 的数组。经过一些尝试,我得到了这个:

type OnlyObjectProperties<T> = {
  [key in keyof T]: T[key] extends object ? key : never;
}[keyof T];

export type ClassRelation<T> = {
  [key in keyof Pick<T, OnlyObjectProperties<T>>]: string;
};

但我希望 OnlyObjectProperties<T> 仅 return 类型为 class 的键。

例如:

class Person{
    name: string;
    task: Task // consider that Task class is defined.
}

const test: ClassRelation<Person> = {tasks: 'tasks'} // only the task key should appear and be allowed on the test variable 

我已经试过这样的东西了:

type OnlyObjectProperties<T> = {
  [key in keyof T]: T[key] extends new(...args: any[]) => any? key : never;
}[keyof T];

但是没有成功。

总而言之,我想要给定类型的键,其类型为 Class

这是我一直用来制作原型的 stackblitz https://stackblitz.com/edit/ts-node-gckhuf?file=index.ts

您无法真正将 class 的实例与 TypeScript 或 JavaScript 中的任何其他 object 区分开来。所有对象都有 constructor 属性,甚至像 Date 这样原生提供的对象也是如此。相反,您需要找到一些方法来识别您想要保留的 class 个实例,并将它们 structurally 与其他对象类型区分开来。

您提供的示例 classes 如下所示:

class User {
  id!: number;
  createdAt!: Date;
  tasks!: Task[];
}
class Task {
  id!: number;
  doneAt!: Date;
  user!: User
}

两者都有 id 属性 类型 number。如果这是区分好的 class 实例和坏实例的好方法,那么您可以相应地更改类型函数:

export type ClassRelation<T> = {
  [K in keyof Pick<T, OnlyPropertiesWithANumericId<T>>]: string;
};

type OnlyPropertiesWithANumericId<T> = {
  [K in keyof T]: T[K] extends { id: number } ? K : never;
}[keyof T];

您可以验证它是否按预期工作:

const onlyRelationsFromTask: ClassRelation<Task> = {
  user: "user"
}

Playground link to code