Conditional types with TypeScript

纵然是瞬间 提交于 2019-12-06 05:07:55

问题


Say I have this:

type TypeMapping = {
  Boolean: boolean,
  String: string,
  Number: number,
  ArrayOfString: Array<string>,
  ArrayOfBoolean: Array<boolean>
}

export interface ElemType {
  foo: keyof TypeMapping,
  default: valueof TypeMapping
}

instead of using any for default, I want to conditionally define it, I tried this:

export interface ElemType<T extends TypeMapping> {
  foo: keyof T,
  default: T
}

but that doesn't seem quite right, does anyone know the right way to do this?

if it's not clear, for any given object that has type ElemType, the key that foo points to, must be matched by the value that foo points to. For example, this is valid:

{
  foo: 'String',
  default: 'this is a string'
}

but this is not:

{
  foo: 'Boolean',
  default: 'this should be a boolean instead'
}

so the type of the default field is conditional on the value/type of the type field.

Succintly, if foo is 'ArrayOfBoolean', then default should be: Array<boolean>. If foo is 'Number', then default should be number, if foo is 'Boolean' then default should be boolean, etc etc.


回答1:


You can define ElemType as in Catalyst's answer and then use a mapped type to take the union of ElemType for all possible K:

interface ElemType<K extends keyof TypeMapping> {
  foo: K;
  default: TypeMapping[K];
}
type ElemTypeMap = {[K in keyof TypeMapping]: ElemType<K>};
// type ElemTypeMap = {
//   Boolean: {foo: "Boolean", default: boolean},
//   String: {foo: "String", default: string},
//   ...
// }
type SomeElemType = ElemTypeMap[keyof TypeMapping];
// Look up in ElemTypeMap by all keys and take the union:
// {foo: "Boolean", default: boolean} | {foo: "String", default: string} | ...



回答2:


You have to tell typescript to validate the actual obj somehow, and you can't really escape without using generics; this is how I would do it:

type TypeMapping = {
  Boolean: boolean;
  String: string;
  Number: number;
  ArrayOfString: Array<string>;
};

export interface ElemType<K extends keyof TypeMapping> {
  foo: K;
  default: TypeMapping[K];
}

const Elem = <E extends keyof TypeMapping, T extends ElemType<E>>(t: ElemType<E>) => t;

Elem({ foo: "Boolean", default: true }); //yup
Elem({ foo: "Boolean", default: "" }); //nope


来源:https://stackoverflow.com/questions/52175508/conditional-types-with-typescript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!