查看以下代码:
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
她,但我不确定如何使用,而且我可能在那里走错了路。
答案 0 :(得分:2)
对于 rnaturalearth
中的每个 A
,您希望 {prop: K; value: X[K]}
成为 K
的 union,如下所示:
keyof X
在该联合的每个元素中,type A = {
prop: "a";
value: string;
} | {
prop: "b";
value: number;
} | {
prop: "c";
value: boolean;
};
类型和 prop
类型之间存在相关性,这禁止您分配来自不同的 value
和 prop
类型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];
的上述定义是等效的,但现在如果您修改 A
,A
将自动更新。
答案 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