映射类型 - >类型,类似于string->类型通过keyof

时间:2017-09-12 19:47:39

标签: typescript

在TypeScript中,我可以定义以下类型:

interface FooNameMapping {
    Bar: {bar: string};
    Baz: {baz: number};
}

我可以在签名中使用的键和映射类型:

function mapped<K extends keyof FooNameMapping>(arg: K): FooNameMapping[K] {
    return '' as any;
}

并致电如下:

let x = mapped('Bar'); //typed as {bar: string}

link

但是,这仅适用于从字符串参数映射的类型。有什么方法可以为从另一种类型映射的类型做类似的事情吗?

换句话说,我有以下重载:

interface EnumeratorConstructor {
    new(col: Excel.Actions): Enumerator<Excel.Action>;
    new(col: Excel.AddIns | Excel.AddIns2): Enumerator<Excel.AddIn>;
    new(col: Excel.AllowEditRanges): Enumerator<Excel.AllowEditRange>;
    new(col: Excel.Areas | Excel.Ranges | Excel.Range): Enumerator<Excel.Range>;
    new(col: Excel.Borders): Enumerator<Excel.Border>;
}

我能否以某种方式创建一个从Excel.Actions映射到Excel.Action的类型,并在单个重载中使用该类型?

1 个答案:

答案 0 :(得分:1)

据我所知,目前没有非hacky方法可以做到这一点,并使自动推理正常工作。 TypeScript目前缺少type-level functions or type families,这是您直接实现此功能所需的。 TypeScript有一个typeof运算符,如果extended适用于任意表达式,尤其是function application,则可以满足您的目的。

你现在可以做的是做一个这样的类型:

interface Example {
    a: {from: string, to: number};
    b: {from: number, to: string};
}
declare function typeMapping<K extends keyof Example>(x: Example[K]['from']): Example[K]['to'];

但问题是K不会自动推断为'a''b',但除非您明确指定,否则将始终只是'a'|'b'

let str: string;   
str = typeMapping(123); // error, return value is string|number
str = typeMapping<'b'>(123); // okay, but need to specify 'b'
str = typeMapping<'a'>(123); // error, 1 is not a string

但这比使用函数重载要糟糕得多。

执行此操作的hacky方法是使用declaration merging来扩充您用作函数输入的内容的类型,并明确地为它们提供输出类型的幻像属性。例如:

import * as Excel from 'excel'; // guessing at location

// add phantom property
declare module 'excel' {
  interface Actions {
    __outputType: Excel.Action
  }
  interface AddIns {
    __outputType: Excel.AddIn
  }
  interface AddIns2 {
    __outputType: Excel.AddIn
  }
  interface AllowEditRanges {
    __outputType: Excel.AllowEditRange
  }
  // ... etc
}

type Inputs = Excel.Actions | Excel.AddIns | Excel.AddIns2 | Excel.AllowEditRanges // ...

// here's the function now    
declare function constructEnumerator<I extends Inputs>(i: I): Enumerator<I['__outputType']>; 

declare let actions: Excel.Actions;
const ret = constructEnumerator(actions); // returns Enumerator<Excel.Action> as desired

这一切都可以正常工作,但是你可能会发现自己有TypeScript抱怨你用来创建任何输入类型的对象文字中缺少__outputType,所以你需要处理那个(例如,文字之后的as Excel.Actions)。值得吗?

那么,你是多么迫切需要这个?除非TypeScript中出现类型级函数,否则可以处理重载或特定于较少的类型?或者模块扩充是否会成为可行的方法?我想,这取决于你。

无论如何,希望有所帮助;祝你好运!