我有一個函式,它接受一個列舉值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>'.
}
}
游樂場鏈接
uj5u.com熱心網友回復:
您需要確保無效狀態是不可表示的。您可以使用其余引數而不是通用引數。
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> - 創建一個允許的元組的聯合。由于其余引數只不過是一個元組,所以它的作用就像一個魅力。
關于getter. 您應該在if條件內定義它或使其成為單獨的功能。SO,隨意移動getter的范圍了func。
如果你想堅持使用泛型,就像在你原來的例子,你應該type和data一個datastracture的一部分,然后用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
}
}
但是問題getter仍然存在,因為它依賴于 uninfered obj.data。您可能需要遷出getter的func范圍和提供額外的引數data或移動getter內conditional statement(不推薦)。
但是,您可以每晚在 TS playground 切換到 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
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/370694.html
