我有一个函数,它接受一个枚举值作为T
,并接受一个在两种数据类型之间进行选择的泛型类型Data<T>
。
我希望能够在一个条件中访问BarData
类型的属性,该条件应该使T
已知。但是,它仍然将data
读取为并集类型。
代码按预期工作,但我必须更改什么才能消除打字脚本错误?
enum DataType { Foo, Bar }
interface FooData { someKey: string }
interface BarData extends FooData { otherKey: string }
type Data<T extends DataType> = T extends DataType.Foo ? FooData : BarData
function func<T extends DataType>(type: T, data: Data<T>): void {
const getter = <K extends keyof Data<T>>(key: K): Data<T>[K] => data[key]
if (type === DataType.Bar) {
data; // still inferred as Data<T>
console.log(data.otherKey) // error Property 'otherKey' does not exist on type 'FooData | BarData'.
console.log(getter('otherKey')) // error Argument of type 'string' is not assignable to parameter of type 'keyof Data<T>'.
}
}
游乐场链接
您需要确保无效状态是不可代表的。可以使用rest参数而不是泛型参数。
enum DataType { Foo = 'Foo', Bar = 'Bar' }
interface FooData { someKey: string }
interface BarData extends FooData { otherKey: string }
type MapStructure = {
[DataType.Foo]: FooData,
[DataType.Bar]: BarData
}
type Values<T> = T[keyof T]
type Tuple = {
[Prop in keyof MapStructure]: [type: Prop, data: MapStructure[Prop]]
}
// ---- > BE AWARE THAT IT WORKS ONLY IN T.S. 4.6 < -----
function func(...params: Values<Tuple>): void {
const [type, data] = params
const getter = <Data, Key extends keyof Data>(val: Data, key: Key) => val[key]
if (type === DataType.Bar) {
const foo = type
data; // BarData
console.log(data.otherKey) // ok
console.log(getter(data, 'otherKey')) // ok
console.log(getter(data, 'someKey')) // ok
}
}
游乐场
MapStructure
-仅用于映射具有有效状态的密钥。
Values<Tuple>
-创建允许元组的并集。由于rest参数只不过是一个元组,所以它就像一个魅力。
关于getter
。您应该在if
条件中定义它,或者使它成为单独的函数。因此,请随意将getter
移出func
的范围。
如果你想坚持使用泛型,就像你原来的例子一样,你应该把type
和data
作为一个数据包的一部分,然后使用typeguard
enum DataType { Foo, Bar }
interface FooData { someKey: string }
interface BarData extends FooData { otherKey: string }
type Data<T extends DataType> = T extends DataType.Foo ? FooData : BarData
const isBar = (obj: { type: DataType, data: Data<DataType> }): obj is { type: DataType.Bar, data: BarData } => {
const { type, data } = obj;
return type === DataType.Bar && 'other' in data
}
function func<T extends DataType>(obj: { type: T, data: Data<T> }): void {
const getter = <K extends keyof Data<T>>(key: K): Data<T>[K] => obj.data[key]
if (isBar(obj)) {
obj.data // Data<T> & BarData
console.log(obj.data.otherKey) // ok
}
}
但CCD_ 14的问题仍然存在,因为它依赖于未感染的CCD_。您需要将func
作用域中的getter
移出并为data
提供额外的参数,或者将getter
移入conditional statement
(不推荐(。
但是,您可以每晚在TS操场切换到TypeScript,并使用对象类型作为参数:
enum DataType { Foo, Bar }
interface FooData { someKey: string }
interface BarData extends FooData { otherKey: string }
type Data = { type: DataType.Foo, data: FooData } | { type: DataType.Bar, data: BarData }
function func(obj: Data): void {
const { type, data } = obj;
const getter = <K extends keyof typeof data>(key: K): typeof data[K] => data[key]
if (type === DataType.Bar) {
data // BarData
console.log(obj.data.otherKey) // ok
}
}
游乐场
getter
仍然不能以您期望的方式工作,因此我建议将其从func
中移出