【问题标题】:How to make Typescript infer the keys of an object but define type of its value?如何使 Typescript 推断对象的键但定义其值的类型?
【发布时间】:2019-07-03 01:04:10
【问题描述】:

我想定义一个对象的类型,但让 typescript 推断键并且没有太多开销来创建和维护所有键的 UnionType。

键入一个对象将允许所有字符串作为键:

const elementsTyped: { 
    [key: string]: { nodes: number, symmetric?: boolean }
} = {
    square: { nodes: 4, symmetric: true },
    triangle: { nodes: 3 }
}

function isSymmetric(elementType: keyof typeof elementsTyped): boolean {
    return elementsTyped[elementType].symmetric;
}
isSymmetric('asdf'); // works but shouldn't

推断整个对象将显示错误并允许所有类型的值:

const elementsInferred = {
    square: { nodes: 4, symmetric: true },
    triangle: { nodes: 3 },
    line: { nodes: 2, notSymmetric: false /* don't want that to be possible */ }
}

function isSymmetric(elementType: keyof typeof elementsInferred): boolean {
    return elementsInferred[elementType].symmetric; 
    // Property 'symmetric' does not exist on type '{ nodes: number; }'.
}

我得到的最接近的是这个,但它不想像这样维护一组键:

type ElementTypes = 'square' | 'triangle'; // don't want to maintain that :(
const elementsTyped: { 
    [key in ElementTypes]: { nodes: number, symmetric?: boolean }
} = {
    square: { nodes: 4, symmetric: true },
    triangle: { nodes: 3 },
    lines: { nodes: 2, notSymmetric: false } // 'lines' does not exist in type ...
    // if I add lines to the ElementTypes as expected => 'notSymmetric' does not exist in type { nodes: number, symmetric?: boolean }
}

function isSymmetric(elementType: keyof typeof elementsTyped): boolean {
    return elementsTyped[elementType].symmetric;
}
isSymmetric('asdf'); // Error: Argument of type '"asdf"' is not assignable to parameter of type '"square" | "triangle"'.

有没有更好的方法来定义对象而不维护键集?

【问题讨论】:

标签: typescript


【解决方案1】:

所以你想要一些推断键但限制值类型并使用excess property checking 来禁止额外属性的东西。我认为获得这种行为的最简单方法是引入一个辅助函数:

// Let's give a name to this type
interface ElementType {
  nodes: number,
  symmetric?: boolean
}

// helper function which infers keys and restricts values to ElementType
const asElementTypes = <T>(et: { [K in keyof T]: ElementType }) => et;

此辅助函数 infers 是来自 et 映射类型的 T。现在你可以像这样使用它了:

const elementsTyped = asElementTypes({
  square: { nodes: 4, symmetric: true },
  triangle: { nodes: 3 },
  line: { nodes: 2, notSymmetric: false /* error where you want it */} 
});

生成的elementsTyped 的类型将(一旦修复错误)具有推断键squaretriangleline,其值为ElementType

希望对你有用。祝你好运!

【讨论】:

  • 好的,我担心它可能需要帮助。如果没有打字稿可以做到这一点,那将是惊人的。我也只能将它用于这个用例。有没有办法让ElementType 也通用?我无法不破坏关键推理或过多的属性检查(感谢关键字;))
  • 如果我可以使用通用的asTypedObject&lt;MyValueType&gt;(...) 函数,那就太好了。这里:asElementTypes= asTypedObject&lt;ElementType&gt;.
  • 在没有partial type parameter inference的情况下可以使用currying来实现:const asTypedObject = &lt;E&gt;() =&gt; &lt;T&gt;(et: { [K in keyof T]: E }) =&gt; et; const asElementType = asTypedObject&lt;ElementType&gt;();
  • 可能更接近您正在寻找的内容,但已回答问题的评论部分可能不是一个很好的论坛。如果您真的有兴趣得到这个问题的答案,那么可能值得提出一个新问题,以便您更多地关注它......当然,在您搜索之后,因为“如何要求类型参数”可能会在其他地方得到回答.
  • @Jack: function asElementTypes() { return function (obj: { [K in keyof Obj]: T }) { return obj; }; }
猜你喜欢
  • 1970-01-01
  • 2021-12-08
  • 2022-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多