打字稿:通过同一对象的其他属性引用属性类型

时间:2021-07-06 17:12:01

标签: typescript

查看以下代码:

interface X {
    a: string;
    b: number;
    c: boolean;
}

type A = {
    prop: keyof X;
    value: X[keyof X];
}

const a: A = { prop: 'a', value: 'a' }; // ok
const b: A = { prop: 'a', value: 1 }; // not ok, value should be string, because X['a'] is string
const c: A = { prop: 'b', value: 1 }; // ok
const d: A = { prop: 'c', value: 1 }; // not ok, value should be boolean, because X['c'] is string

这里我希望 .value 属性的类型为 string,如果 prop"a"number"b" 和 { {1}} 用于 boolean,但对于所有情况,它都是 "c",因为 string|number|boolean 可以为 keyof X 的每次使用引用不同的键。我怎样才能让它两次引用同一个属性,而不是将它显式输入到 type A 的泛型参数中?

我觉得我应该使用 A 她,但我不确定如何使用,而且我可能在那里走错了路。

2 个答案:

答案 0 :(得分:2)

对于 rnaturalearth 中的每个 A,您希望 {prop: K; value: X[K]} 成为 Kunion,如下所示:

keyof X

在该联合的每个元素中,type A = { prop: "a"; value: string; } | { prop: "b"; value: number; } | { prop: "c"; value: boolean; }; 类型和 prop 类型之间存在相关性,这禁止您分配来自不同的 valueprop 类型value 的成员:

X

您还可以通过多种方式让编译器以编程方式为您计算此值,例如构建一个 mapped type,您可以立即index into

const a: A = { prop: 'a', value: 'a' }; // ok
const b: A = { prop: 'a', value: 1 }; // error
const c: A = { prop: 'b', value: 1 }; // ok
const d: A = { prop: 'c', value: 1 }; // error

可以通过 IntelliSense 验证 type A = { [K in keyof X]-?: { prop: K; value: X[K]; } }[keyof X]; 的上述定义是等效的,但现在如果您修改 AA 将自动更新。

Playground link to code

答案 1 :(得分:1)

我不相信没有泛型你可以做到这一点。所以这里是:

type A<K extends keyof X> = {
    prop: K;
    value: X[K];
}

好消息是,您只需要在分配给显式给定类型的变量时指定泛型参数,如下所示:

const a: A<'a'> = { prop: 'a', value: 'a' };

而且您的代码中可能永远不会有这种需求。例如,如果您使用此泛型类型指定函数,则无需显式指定泛型参数即可使其工作:

function fn<K extends keyof X>(a: A<K>) {
    // something
}

fn({ prop: 'a', value: 'a' }); // ok
fn({ prop: 'a', value: 1 }); // not ok, value should be string, because X['a'] is string
fn({ prop: 'b', value: 1 }); // ok
fn({ prop: 'c', value: 1 }); // not ok, value should be boolean, because X['c'] is string

Playground link