如何确保对象值是由 TypeScript 的对象键推导出的类型的键?

问题描述 投票:0回答:1

鉴于

// GIVEN

type Animal<T extends string> = {
    id: T,
}

type Dog = Animal<"animal.dog"> & {
   foo: string
}

type Cat = Animal<"animal.cat"> & {
   bar: string
}

type MyType<T extends Dog | Cat = Dog | Cat> = { [K in T["id"]]: keyof T };

type MyTypeVariant<C extends "animal.dog" | "animal.cat" = "animal.dog" | "animal.cat"> = { [K in C]: keyof Animal<C> };

// EXPECT TO BE OK

const obj: MyType = {
    "animal.dog": "foo",
    "animal.cat": "bar"
}

// EXPECT TO FAIL

const fail: MyType = {
    "animal.dog": "bar",
    "animal.cat": "foo"
}

如果

value
(例如:“foo”)不是类型(例如:Cat)的键,可以从
key
(例如:“ obj 的 Animal.cat")?

现在我收到错误

类型“foo”不能分配给类型“id”

因为“id”是

Dog
Cat
之间唯一的共同属性。

我想我需要在这里进行类型推断。

有什么想法吗?

typescript typescript-generics type-inference template-literals typescript-template-literals
1个回答
0
投票

您要找的类型是

type Type =
  { [T in Dog | Cat as T["id"]]: keyof T }

评估为

type Type = {
    "animal.dog": "id" | "foo";
    "animal.cat": "id" | "bar";
}

这使用映射类型中的键重新映射来迭代union

Dog | Cat
,对于该联合的每个成员
T
,我们使用
T["id"]
作为键,
keyof T
作为值。由于我们迭代
T
的成员,因此键和值之间的相关性被保留。


如果我们需要按照您的方式进行操作,即在

K
而不是
T["id"]
上迭代
T
,我们需要检查
K
Extract
T
的正确成员,也许像这样:

type TypeGen<T extends Dog | Cat = Dog | Cat> =
  { [K in T["id"]]: keyof Extract<T, { id: K }> }

type Type = TypeGen
/* type Type = {
    "animal.dog": "id" | "foo";
    "animal.cat": "id" | "bar";
} */

但是按键重新映射更简单。

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.