如何从构造函数列表初始化TypeScript对象? (也许:Mapped Type?类型运算符与`typeof`相反?)

时间:2017-09-06 00:01:55

标签: typescript types

在TypeScript class Foo {}中生成名称声明Foo,它是类Foo的实例的类型,它生成变量Foo,它是构造函数Foo类的函数。 (TypeScript Declaration Spaces。)给定值Foo(构造函数)可以使用typeof运算符来获取类的类型(而不是类的实例的类型) 。

要点:

Code            Variable Declaration         Name Declaration
--------------- ---------------------------- ----------------
class Foo {}    variable Foo                 type Foo
typeof Foo      type of variable Foo

我想知道的是:给定变量Foo的类型,是否有编译时运算符来获取类型Foo,类Foo的实例类型}?

或者,更一般地说,我如何简洁地描述getWhatIWant的返回类型(下方)?

考虑:

class Base {
  constructor(someArgument: string) {}
}

class A extends Base { _brand: 'A' }
class B extends Base { _brand: 'B' }
class C extends Base { _brand: 'C' }

let allOfTheLetters = { A, B, C };

我想要一个带有构造函数名称的对象作为键,并将每个类的实例作为值。文字版本是:

let whatIWouldLike = {
  A: new A('same params'),
  B: new B('same params'),
  C: new C('same params')
}

我试图将allOfTheLetters转换为whatIWouldLike。它在JavaScript中没有问题 - 迭代键和值:

function getWhatIWant(letters) {
  let result = {};
  Object.keys(letters).forEach((key) => result[key] = new letters[key]('same params'));
  return result;
}

如何描述此功能的返回类型?映射类型让我接近:

type TypeIWant = {[key in keyof typeof allOfTheLetters ]: typeof allOfTheLetters [key]};

let example: TypeIWant;
let test: 'B' = example.B._brand; // Error: Property '_brand' does not exist on type 'typeof B'

example.A的类型为'typeof A',而不是我需要的'A'

如果instanceof是可以转换类型的编译时运算符,那么我可以将类型'typeof A'转换为'A',并且我得到了我想要的内容。但这不是它的工作原理。

如何在不写出每个键值对的情况下描述getWhatIWant函数返回的对象类型,就像我创建whatIWouldLike一样?

TypeScript Playground (same code as in this question)

1 个答案:

答案 0 :(得分:3)

我认为您与映射类型非常接近。让我们来看看你的课程:

class Base {
    constructor(someArgument: string) {}
}

class A extends Base { _brand: 'A'; }
class B extends Base { _brand: 'B'; }
class C extends Base { _brand: 'C'; }

let allOfTheLetters = { A, B, C };

好的,所以你的所有类都有一个带有一个string参数的构造函数。让我们来描述一下:

type BaseConstructor<T> = new (someArgument: string) => T;

现在让我们根据allOfTheLetters的输出描述getWhatIWant()的类型。是的,我正在做backwards。如果输出是一个值为实例的对象,则输入是一个对象,其值是那些非常相同的实例的构造函数,按键映射:

type DictionaryOfConstructors<T> = {
  [K in keyof T]: BaseConstructor<T[K]>
}

现在我们可以定义getWhatIWant()的类型:

function getWhatIWant<T>(letters: DictionaryOfConstructors<T>): T {
  let result: any = {};
  // note I have to assert that `key` is `keyof T` below
  Object.keys(letters).forEach((key:keyof T) => result[key] =
    new letters[key]('same params'));
  return result;
}

let example = getWhatIWant(allOfTheLetters);
let test: 'B' = example.B._brand; // it works!

这是有效的原因是因为给定实例类型(T变为new(...)=>T)比描述构造函数类型的实例类型更容易描述构造函数类型(在TypeScript,for now anyway中没有typeof new C(...),幸运的是inference from mapped types可以为我们处理向后映射。

希望有所帮助;祝你好运!