Typescript能在条件块中推断出泛型类型的键吗



我有一个函数,它接受一个枚举值作为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的范围。

如果你想坚持使用泛型,就像你原来的例子一样,你应该把typedata作为一个数据包的一部分,然后使用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中移出

最新更新