首页 > 解决方案 > TypeScript:如何准确地为复杂对象键入函数(带有索引签名)

问题描述

概述

我有一个函数需要返回对象中的特定位置。这里没有什么不寻常的。我面临的问题是该对象是已知索引和索引签名的混合,并且该函数似乎变得混乱。我希望我只是在做一些愚蠢的事情并输入错误的功能,并且你们中的一个好人可以让我知道我做错了什么......但这感觉就像一个 TS linter 错误。任何帮助将不胜感激(包括尽可能简化......但我真的只关心解决我的特定打字问题)。

功能

function getCounts<Entity extends EntityType>(entity: Entity, id: string): Counts[Entity][string] {
  return counts[entity][id] ?? []; // throws the error
}

错误

Type 'CountsWithEntity<Entity1> | CountsWithEntity<Entity2>' is not assignable to type 'Counts[Entity][string]'.
  Type 'CountsWithEntity<Entity1>' is not assignable to type 'Counts[Entity][string]'.

但它有效!

![在此处输入图像描述

并且推断的类型,在使用函数时,是正确的

在此处输入图像描述

娱乐链接

打字稿游乐场

完整代码(娱乐链接中的相同代码)

type Entity1 = { hello: string };
type Entity2 = { world: string };
enum EntityType {
  entity1 = 'hello',
  entity2 = 'world',
}
type Entities = Entity1 | Entity2;
type EntityMap = {
  [EntityType.entity1]: Entity1;
  [EntityType.entity2]: Entity2;
};

type CountsWithEntity<Entity extends Entities> = Array<{
  count: number;
  entity: Entity;
}>;
type Counts = {
  [entityKey in EntityType]: {
    [id: string]: CountsWithEntity<EntityMap[entityKey]>;
  };
};

const counts: Counts = {
  [EntityType.entity1]: {
    id1: [
      {
        count: 2,
        entity: { hello: 'hi' },
      },
      {
        count: 1,
        entity: { hello: 'you' },
      },
    ],
    id2: [
      {
        count: 3,
        entity: { hello: 'bye' },
      },
      {
        count: 4,
        entity: { hello: 'cya' },
      },
    ],
  },
  [EntityType.entity2]: {
    id3: [
      {
        count: 6,
        entity: { world: 'earth' },
      },
      {
        count: 5,
        entity: { world: 'place' },
      },
    ],
    id4: [
      {
        count: 8,
        entity: { world: 'dirt' },
      },
      {
        count: 7,
        entity: { world: 'huh?' },
      },
    ],
  },
};

function getCounts<Entity extends EntityType>(entity: Entity, id: string): Counts[Entity][string] {
  return counts[entity][id] ?? []; // throws the error
}

console.log(counts[EntityType.entity1].id1); // expected `[{ count: 2, entity: { hello: 'hi' } }, { count: 1, entity: { hello: 'you' } }]` - success, no TS errors
console.log(getCounts(EntityType.entity1, 'id1')); // expected `[{ count: 2, entity: { hello: 'hi' } }, { count: 1, entity: { hello: 'you' } }]` - success, no TS errors
console.log(getCounts(EntityType.entity2, 'id3')[0].entity.world); // expected `"earth"` - success, no TS errors

标签: typescripttypescript-typingstypescript-generics

解决方案


该错误告诉您您的返回类型应该是CountsWithEntity<Entity1> | CountsWithEntity<Entity2>.

那是因为类型counts[entity][id]CountsWithEntity<EntityMap[entityKey]>

  • 尝试以下
function getCounts<Entity extends EntityType>(entity: Entity, id: string): CountsWithEntity<Entity1> | CountsWithEntity<Entity2> {
  return counts[entity][id] ?? []; // throws the error
}

此外

您还可以使用返回类型CountsWithEntity<EntityMap[EntityType]>更加动态。

function getCounts<Entity extends EntityType>(entity: Entity, id: string): CountsWithEntity<EntityMap[EntityType]> {
  return counts[entity][id] ?? []; // throws the error
}

推荐阅读