如何保留对象的原始索引标记,但仍然指定其值的类型?

huangapple go评论61阅读模式
英文:

How can I keep the original index signature of an object, but still specify the type of its values?

问题

我有一个具有字符串键和类型化值的大对象。我想为这个对象添加类型。

interface Animal {
    legs: 0 | 1 | 2 | 3 | 4;
}

const Animals: Record<string, Animal> = {
    snake: { legs: 0 },
    dog: { legs: 4 },
    bird: { legs: 2},
    // 更多条目...
};

如果我将对象类型化为Record<string, Animal>,那么所有字符串都变成有效的键。因此,没有防止我意外地写入Animals.birb的保护措施。像Record<"snake" | "dog" | "bird" ... , Animal>这样做是愚蠢的,因为对象很大且具有许多键。

如果我不显式编写类型定义,TypeScript将推断类型为{ snake: { legs: number}, dog: { legs: number}, ... }

这是不好的,因为我需要TypeScript知道值的类型是Animal,而不仅仅是{ legs: number }

即类型应该是{ snake: Animal, dog: Animal, ... }

为每一行编写as Animal是愚蠢的。

有没有更好的方法来为这个对象添加类型?

英文:

I have a large object with string keys and typed values. I want to type this object.

interface Animal {
    legs: 0 | 1 | 2 | 3 | 4;
}

const Animals: Record&lt;string, Animal&gt; = {
    snake: { legs: 0 },
    dog: { legs: 4 },
    bird: { legs: 2},
    // many more entries...
};

If I type the object as Record&lt;string, Animal&gt;, then all strings become valid keys. So there is no protection against me accidentally writing Animals.birb. It would be silly to do something like Record&lt;&quot;snake&quot; | &quot;dog&quot; | &quot;bird&quot; ... , Animal&gt; because the object is big and has many keys.

If I do not explicitly write a type definition, then TypeScript will infer the type as
{ snake: { legs: number}, dog: { legs: number}, ... }.

This is bad because I need TypeScript to know that the type of value is Animal and not just { legs: number },

i.e., the type should be { snake: Animal, dog: Animal, ... }

Writing as Animal for every line would be silly.

Is there a better way to type this object?

答案1

得分: 1

以下是您要翻译的代码部分:

如果您想要确保变量可以分配给一种类型而不标注为该类型,并且潜在地丢失信息(比如特定的键),您可以使用满足运算符 satisfies

const Animals = {
    snake: { legs: 0 },
    dog: { legs: 4 },
    bird: { legs: 2 },
} satisfies Record<string, Animal>;

/* const Animals: {
    snake: {
        legs: 0;
    };
    dog: {
        legs: 4;
    };
    bird: {
        legs: 2;
    };
} */

您可以看到 Animals 已被推断出具有已知的键,以及其 legs 属性的适当狭义文字类型,而不是原本推断的较宽泛的 number 类型。

这也会捕获初始化器中的错误,类似于标注:

const badAnimals = {
    snake: { legs: 0 },
    dog: { legs: 4 },
    bird: { legs: 2 },
    octopus: { legs: 8 } // 错误!
} satisfies Record<string, Animal>;

Playground 代码链接

英文:

If you want to ensure that a variable is assignable to a type without annotating it as that type and potentially throwing away information (like the particular keys), you can use the satisfies operator:

const Animals = {
    snake: { legs: 0 },
    dog: { legs: 4 },
    bird: { legs: 2 },
} satisfies Record&lt;string, Animal&gt;;

/* const Animals: {
    snake: {
        legs: 0;
    };
    dog: {
        legs: 4;
    };
    bird: {
        legs: 2;
    };
} */

You can see that Animals has been inferred with known keys, and with properties whose legs are of appropriately narrow literal types instead of the wider number type which would have been inferred otherwise.

This also will catch mistakes in your initializer similarly to an annotation:

const badAnimals = {
    snake: { legs: 0 },
    dog: { legs: 4 },
    bird: { legs: 2 },
    octopus: { legs: 8 } // error!
} satisfies Record&lt;string, Animal&gt;;

Playground link to code

huangapple
  • 本文由 发表于 2023年3月7日 04:42:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/75655639.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定