【问题标题】:Typescript how to make a mapped type extends typeof ClassTypescript 如何使映射类型扩展 typeof 类
【发布时间】:2022-01-22 11:54:36
【问题描述】:

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

基本上,我希望类型过滤器只返回具有类类型或类数组的对象的键。经过一些尝试,我最终得到了这个:

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&lt;T&gt; 只返回类型为类的键。

例如:

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

但没有成功。

总而言之,我想要给定类型的键,具有类的类型

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

【问题讨论】:

  • 有一个问题:声明Task 类型的属性不允许检索“是Task 构造函数的实例”信息。它可以是至少具有Task 属性的任何值
  • 您可能正在寻找一个构造签名(当您说“一个类”时,您大概是在谈论一个类构造函数)。看起来您确实尝试过“没有成功”,但没有提供minimal reproducible example 实际发生的情况。您能否将您的代码编辑为任何人都可以粘贴到独立 IDE 中并查看您所看到的内容?
  • 对不起@GuerricP 但我不明白你的意思。你能再解释一下吗?
  • @jcalz 我尝试提供一些可以粘贴到 IDE 中的代码。但我能提供的东西并不多,我很害怕。我不知道如何“调试” TS 行为,除了尝试分配对我想要的类型无效的内容
  • 哦,所以你不想要类constructors,你想要类instances。 TypeScript 类型系统无法表示“某个类的实例”;这几乎没有任何意义,因为每个对象都是其constructor 属性的“实例”,即使是未使用class 语法声明的对象也是如此。所以extends object 是你想要的,就像你原来的定义一样。 Looks fine to me。显然这对你不起作用,因为你在问这个问题,那么问题是什么?如果您无法通过minimal reproducible example 传达问题,我不确定我能做多少????

标签: typescript mapped-types


【解决方案1】:

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 2019-01-15
    • 1970-01-01
    • 2018-01-03
    • 2023-03-18
    • 1970-01-01
    • 2016-01-29
    • 1970-01-01
    • 2018-06-25
    • 2021-03-25
    相关资源
    最近更新 更多