首页 > 解决方案 > 具有相同键和相似值的两种类型的打字稿类型

问题描述

我有一个将对象作为构造函数参数的类,我想在类上强制执行一个方法以接受一个非常相似的对象。对象具有任意键。

例如,使用此对象进行构造:

{
  foo: { type: 'A' },
  bar: { type: 'B'}
}

我希望该方法只接受类似形式的对象,即具有相同的键,并且每个键的值类型与初始对象兼容。喜欢:

{
  foo: SomeARelatedThing,
  bar: SomeBRelatedThing
}

我有一个解决方法,我至少可以强制执行相同的键,然后进行大量类型检查(无论如何都很好!)以确保值实际上匹配。

这是我的用例中的一个人为示例:

type TypeName = 'A' | 'B' | 'C' // ...

class Action<K extends TypeName> { 
  type: K
  constructor(type: K) { this.type = type } 
}
type AnyAction = Action<'A'> | Action<'B'> | Action<'C'> // ...

type AProp = { type: 'A' }
type BProp = { type: 'B' }
type CProp = { type: 'C' }
type AnyProp = AProp | BProp | CProp // ...

type PropMap<K extends string> = Record<K, AnyProp>
type ActionMap<K extends string> = Record<K, AnyAction>

class Thing<K extends string> {
  props: PropMap<K>
  constructor(props: PropMap<K>) { this.props = props }

  myMethod<>(actions: ActionMap<K>) { /* ... */ }
}

// type = Thing<'foo' | 'bar'>
const thing = new Thing({
  foo: { type: 'A' },
  bar: { type: 'B'}
})

// the keys are enforced, but how can the values of foo and bar be enforced, too
thing.myMethod({
  foo: new Action('A'),
  bar: new Action('B'),
}) 

我想我想要一个更像等于的类型Thing<{foo: 'A', bar: 'B'}>,但我不知道如何有条件地从构造函数的PropMap-like 输入计算它Thing,或者即使我这样做了,那么我将如何计算正确ActionMap的 -like 类型。

MyMethod实际上接受 aPartial<ActionMap<K>>但我认为这对我的要求并不重要。

标签: javascripttypescript

解决方案


我想我明白了。您需要使用映射类型

class Thing<K extends string, P extends PropMap<K>> {
  props: P
  constructor(props: P) { this.props = props }

  myMethod(actions: {[Property in keyof P]: Action<P[Property]["type"]>}) { return actions }
}

const thing = new Thing({
  foo: { type: 'A' },
  bar: { type: 'B'}
})

// the keys are enforced, as well as the corresponding Action types
thing.myMethod({
  foo: new Action('A'),
  bar: new Action('B'),
}) 

请注意,您需要将泛型添加P到您的类中,否则 TypeScript 在您稍后Thing实例化时无法推断出更详细的信息。Thing基本上,您需要将其设置P为一致的泛型类型 Thing否则无法将PropMap其与其扩展的类型区分开来。

然后,魔术发生在actions: {[Property in keyof P]: Action<P[Property]["type"]>}. 让我们分解一下:

  1. [Property in keyof P]:映射类型索引签名。这就是让我们可以访问 中的特定键的原因P,例如foobar等。
  2. Action<...>将与我们在上面 (1.​​) 中映射的每个键对应的值设置为某个值,我们希望将其设置为ActionAction<'A'>我们希望操作从 的原始值派生P,所以...
  3. P[Property]["type"]让我们type从原始类型的键/值对中访问值P。由于Property变化(它从一种类型映射到另一种类型),因此例如它变成foo: P["foo"]["type"]which is 'A', bar: P["bar"]["type"]which is'B'

操场

在此处输入图像描述


推荐阅读