如何使Typescript推断对象的键,但定义其值的类型?

Jac*_*ack 7 typescript

我想定义一个对象的类型,但是让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
Run Code Online (Sandbox Code Playgroud)

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

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; }'.
}

Run Code Online (Sandbox Code Playgroud)

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

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

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

mar*_*eck 13

TypeScript >=4.9.0

TypeScript4.9.0添加了satisfies关键字,可用于在推断键时约束对象的值。

type ElementValue = {
  nodes: number;
  symmetric?: boolean;
};

const elements = {
  square: { nodes: 4, symmetric: true },
  triangle: { nodes: 3 },
} satisfies Record<string, ElementValue>;

type Elements = typeof elements;
type ElementType = keyof Elements;

function isSymmetric(elementType: ElementType): boolean {
  const element = elements[elementType];
  return 'symmetric' in element && element.symmetric;
}

isSymmetric('asdf'); // doesn't work
Run Code Online (Sandbox Code Playgroud)

打字稿 <4.9.0

中间函数可用于在推断键时约束对象的值。

type ElementValue = {
  nodes: number;
  symmetric?: boolean;
};

function typedElements<T extends Record<string, ElementValue>>(o: T) {
  return o;
}

const elements = typedElements({
  square: { nodes: 4, symmetric: true },
  triangle: { nodes: 3 },
});

type Elements = typeof elements;
type ElementType = keyof Elements;

function isSymmetric(elementType: ElementType): boolean {
  const element = elements[elementType];
  return 'symmetric' in element && element.symmetric;
}

isSymmetric('asdf'); // doesn't work
Run Code Online (Sandbox Code Playgroud)


jca*_*alz 5

所以你想要一些推断密钥但限制值类型的东西,并使用多余的属性检查来禁止额外的属性.我认为获得该行为的最简单方法是引入辅助函数:

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

这个辅助函数从映射类型推断出类型.现在您可以像这样使用它:Tet

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

该类型所产生的elementsTyped意志(一旦你修复错误)已经推断出密钥square,triangle以及line与价值ElementType.

希望对你有用.祝好运!

  • 在没有[部分类型参数推断](https://github.com/Microsoft/TypeScript/pull/26349)的情况下可以使用[currying](https://en.wikipedia.org/wiki/Currying)来实现这:`const asTypedObject = &lt;E&gt;() =&gt; &lt;T&gt;(et: { [K in keyof T]: E }) =&gt; et; const asElementType = asTypedObject&lt;ElementType&gt;();` (6认同)
  • 好吧,我担心它可能需要一个帮手。如果打字稿没有它也能做到这一点,那就太棒了。我也只能在这个用例中使用它。有没有办法使`ElementType`也通用?我无法不破坏关键推断或多余的属性检查(感谢关键字;)) (2认同)