打字稿类型:在类型定义中使用泛型的static readonly/const 属性

Typescript types: use static readonly/const property of generic type in type definition

我有这个class定义:

export class Entity {
    a?: string;
    b?: string;
    c?: string;
    static required = ['c'] as const;
}

基本上我想写一个类型定义,它将采用这种类型,并使用静态array required,来生成c的类型] 不是可选的。

我想这样做:

export type WithRequired<T extends Entity> = Partial<T> & Required<Pick<T, typeof T.required[number]>>;

但这失败并出现错误:

'T' only refers to a type, but is being used as a value here.

如果我通过将 T.required 替换为 Entity.required 来稍微更改定义,那么它不会给我一个错误:

export type WithRequired<T extends Entity> = Partial<T> & Required<Pick<T, typeof Entity.required[number]>>;

这在这种情况下有效..但这当然不是一个令人满意的解决方案,因为我必须为项目中“实体”的每个变体创建一个类型,而不是让一个通用类型来处理所有案例。

所以我实际上有 2 个问题,只要回答其中 1 个问题就会对我有很大帮助:

type Entity 是您的 class 实例的类型。 Entity class 的构造函数,typeof Entity 是该构造函数的类型。

并且实例的类型完全与其构造函数的类型脱节。所以如果你只有实例类型,你无法知道构造函数的类型。

事实上,您可以像使用任何接口一样使用 classes 实例类型,这证明实例类型不知道它是由什么构造的。

class Foo { bar: string }
const foo: Foo = { bar: 'baz' } // works

但是,您可以反其道而行之,使用 InstanceType<typeof MyClass>.

从构造函数类型派生实例类型

这意味着您必须捕获 constructor 类型作为泛型,因为这是您的静态值实际所在的位置。然后,您可以从该类型中提取 required static 属性 并将其合并到实例类型中。

export type WithRequired<T extends typeof Entity> =
    Partial<InstanceType<T>> &
    Required<Pick<InstanceType<T>, T['required'][number]>>;

然后像这样使用:

type TestA = WithRequired<typeof Entity>
const testA1: TestA = { c: 'c'} // works
const testA2: TestA = { a: 'a' } // Property 'c' is missing in type '{ a: string; }' but required in type 'Required<Pick<Entity, "c">>'.(2322)

缺点是您必须传入 typeof MyClass,这会稍微冗长一些。但是如果你想访问一个静态的 属性 那么你需要构造函数的类型,你无法绕过这个简单的事实。

Playground