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

ale*_*ird 0 types typescript

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

概括:

代码变量声明名称声明
--------------- ---------------------------------------- ------- ---------
class Foo {} 变量 Foo 类型 Foo
typeof Foo 变量 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 };
Run Code Online (Sandbox Code Playgroud)

我想要一个以构造函数名称作为键的对象,以及每个类的一个实例作为值。字面版本是:

let whatIWouldLike = {
  A: new A('same params'),
  B: new B('same params'),
  C: new C('same params')
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试转换allOfTheLetterswhatIWouldLike. 在 JavaScript 中没有问题 - 迭代键和值:

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

我如何描述这个函数的返回类型?映射类型让我接近:

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'
Run Code Online (Sandbox Code Playgroud)

example.A有类型'typeof A''A'不像我需要的那样。

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

如何描述getWhatIWant函数返回的对象的类型,而不像我创建的那样写出每个键值对whatIWouldLike

TypeScript Playground(与此问题中的代码相同)

jca*_*alz 5

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

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 };
Run Code Online (Sandbox Code Playgroud)

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

type BaseConstructor<T> = new (someArgument: string) => T;
Run Code Online (Sandbox Code Playgroud)

现在让我们将 的类型描述allOfTheLetters为您想要的输出的函数getWhatIWant()。是的,我是倒着做的。如果输出是一个对象,其值为实例,则输入是一个对象,其值为那些非常相同的实例的构造函数,由键映射:

type DictionaryOfConstructors<T> = {
  [K in keyof T]: BaseConstructor<T[K]>
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以定义 的类型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!
Run Code Online (Sandbox Code Playgroud)

这样做的原因是因为在给定实例类型(T变成new(...)=>T)的情况下描述构造函数类型比在给定构造函数类型的情况下描述实例类型更容易(无论如何typeof new C(...),TypeScript 中没有任何东西)并且幸运的是从映射类型推断是那里为我们处理向后映射。

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