将 Typescript 属性设置为 `never` 仍然会将其视为必填
我正在尝试创建一种类型,该类型会在不同阶段逐步被填充。例如,在创建一个 Bar 对象时,我希望 Data<DataType.bar> 包含一个完整的 Foo 对象,但 Bar 本身或者 Baz 还不会是 Data 的一部分:
enum DataType {
'foo',
'bar',
'baz',
}
type Foo = {
a: string;
};
type Bar = {
b: string;
};
type Baz = {
c: string;
};
type Data<T extends DataType | null = null> = {
[DataType.foo]: T extends DataType.bar | DataType.baz | null ? Foo : never;
[DataType.bar]: T extends DataType.baz | null ? Bar : never;
[DataType.baz]: T extends null ? Baz : never;
};
const myData: Data = {
[DataType.foo]: { a: 'I should already exist while bar is being created' },
/*
[DataType.bar]: {
b: `I correctly error when uncommented...
but when commented, DataType.bar also appears to be required?`
}
*/
};
尽管当包含 DataType.bar 时,myData 正确地包含了一个错误,但当它被注释掉时,我也会收到以下错误:
Type '{ 0: { a: string; }; }' is missing the following properties from type 'Data<DataType.bar>': [DataType.bar], [DataType.baz]有没有办法根据 T extends DataType 泛型告诉 TypeScript 这些属性实际上并没有缺失?
编辑:这似乎可以达到我想要的效果,但看起来非常重复?有没有更好的方法来做这个?
type Data<T extends DataType | null = null> = T extends null
? {
[DataType.foo]: Foo;
[DataType.bar]: Bar;
[DataType.baz]: Baz;
}
: T extends DataType.baz
? {
[DataType.foo]: Foo;
[DataType.bar]: Bar;
}
: T extends DataType.bar
? {
[DataType.foo]: Foo;
}
: T extends DataType.foo
? {}
: never;
答案#1
使用 interface 和 extends
enum DataType {
'foo',
'bar',
'baz',
}
interface DataFoo { [x:string]: never}
interface DataBar { foo: string; };
interface DataBaz extends DataBar { bar: string; };
interface DataAll extends DataBaz { baz: string; };
type Data<T extends DataType | null = null> = T extends null ? DataAll
: T extends DataType.baz
? DataBaz
: T extends DataType.bar
? DataBar
: T extends DataType.foo
? DataFoo
: never;
const dataFoo: Data<DataType.foo> = {};
const dataBar: Data<DataType.bar> = { foo: "" };
const dataBaz: Data<DataType.baz> = { foo: "", bar: "" };
const dataAll: Data = { foo: "", bar: "", baz:"" };
(参见 TypeScript Playground)
使用 type 和类型运算符 &
enum DataType {
'foo',
'bar',
'baz',
}
type DataFoo = Record<string,never>;
type DataBar = {foo:string};
type DataBaz = DataBar & {bar:string};
type DataAll = DataBaz & {baz:string};
type Data<T extends DataType | null = null> = T extends null ? DataAll
: T extends DataType.baz
? DataBaz
: T extends DataType.bar
? DataBar
: T extends DataType.foo
? DataFoo
: never;
const dataFoo: Data<DataType.foo> = {};
const dataBar: Data<DataType.bar> = { foo: "" };
const dataBaz: Data<DataType.baz> = { foo: "", bar: "" };
const dataAll: Data = { foo: "", bar: "", baz:"" };
(参见 TypeScript Playground)
`Record` 是一个 TypeScript 工具函数。`Record<string,never>` 与 `{[x:string]:never}` 相同
主要的好处是文本长度随着 O(Nlevels) 增加。在你原来的版本中,它是随着 O(Nlevels^2) 增加的。
