我们说我有以下类型地图:
type MyTypes = {
'float': number;
'text': string;
'bool': boolean;
'price': number;
'date': Date;
};
我想为此自动生成一个有区别的联合类型。相当于:
type Datum<K, V> = { type: K, value: V };
type MagicUnionMaker<TypeMap> = (
// pretend the below is auto-generated from TypeMap
Datum<'float', number> |
Datum<'text', string> |
Datum<'bool', boolean> |
Datum<'price', number> |
Datum<'date', Date>
);
可以这样使用:
interface DataModel<TypeMap> {
data(row: number, col: number): MagicUnionMaker<TypeMap>;
}
let model: DataModel<MyTypes>;
let datum = model.data(0, 0);
switch (datum.type) {
case 'float':
// datum.value is `number`
break;
case 'text':
// datum.value is `string`
break;
case 'bool':
// datum.value is `boolean`
break;
case 'price':
// datum.value is `price`
break;
case 'date':
// datum.value is `Date`
break;
case 'thing': // error
break;
}
目前这样的事情可能吗?
答案 0 :(得分:3)
不完全是,您似乎无法拥有通用的UnionMaker,并且需要在每次需要时就地构建联合类型:
type MyTypes = {
'float': number;
'text': string;
'bool': boolean;
'price': number;
'date': Date;
};
type TypeDataMap<T> = {[K in keyof T]: {type: K, value: T[K]}};
interface MyDataModel {
data(row: number, col: number): TypeDataMap<MyTypes>[keyof MyTypes];
}
let model: MyDataModel;
let datum = model.data(0, 0);
// type inference works as expected
switch (datum.type) {
case 'float':
let n: number = datum.value;
break;
case 'text':
let s: string = datum.value;
break;
case 'bool':
let b: boolean = datum.value;
break;
case 'price':
let p: number = datum.value;
break;
case 'date':
let d: Date = datum.value;
break;
case 'thing': // error: Type '"thing"' is not comparable to type
// '"float" | "text" | "bool" | "price" | "date"'.
break;
}
尝试构造等效的泛型类型失败:
// does not work
type UnionMaker<T> = TypeDataMap<T>[keyof T];
type MyTypeMap = UnionMaker<MyTypes>;
// because MyTypeMap gets inferred for some reason as
// type MyTypeMap = {
// type: "float" | "text" | "bool" | "price" | "date";
// value: string | number | boolean | Date;
// }
// which is NOT what you want
答案 1 :(得分:0)
似乎这可能在Typescript 2.8中有条件类型,参见https://github.com/Microsoft/TypeScript/pull/21316
npm install
typescript@next
之后,你可以这样做:
type MyTypes = {
'float': number;
'text': string;
'bool': boolean;
'price': number;
'date': Date;
};
type Datum<K, V> = { t: K, v: V };
type FromKeys<T, U> = T extends keyof U ? Datum<T, U[T]> : never;
type UnionMaker2<T, U> = FromKeys<keyof T, U>;
type UnionMaker<T> = UnionMaker2<T, T>;
const x1: UnionMaker<MyTypes> = {t: 'text', v: 'string'};
const x2: UnionMaker<MyTypes> = {t: 'bool', v: true};
// fails, as expected:
// const x3: UnionMaker<MyTypes> = {t: 'text', v: true};