[英]Typescript: Reference type of property by other property of same object
See the following code:请参见以下代码:
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
Here I want the type of the .value
property to be string
, if prop
is "a"
, number
for "b"
and boolean
for "c"
, but instead it is string|number|boolean
for all cases, because keyof X
can refer to different keys for each use for type A
.这里我希望
.value
属性的类型是string
,如果prop
是"a"
, "b"
是number
, "c"
是boolean
,但在所有情况下它都是string|number|boolean
,因为keyof X
可以为type A
的每次使用引用不同的键。 How can I make it refer to the same property twice, without having it explicitly input it into a generic argument of A
?我怎样才能让它引用同一个属性两次,而不是将它显式输入到
A
的通用参数中?
I feel like I should be using infer
her, but I'm not sure how, and also I might be on the wrong track there.我觉得我应该使用
infer
她,但我不确定如何使用,而且我可能走错了路。
You want A
to be a union of {prop: K; value: X[K]}
您希望
A
成为 { prop {prop: K; value: X[K]}
{prop: K; value: X[K]}
for each K
in keyof X
, like this: keyof X
中的每个K
的{prop: K; value: X[K]}
,如下所示:
type A = {
prop: "a";
value: string;
} | {
prop: "b";
value: number;
} | {
prop: "c";
value: boolean;
};
In each element of that union, there is a correlation between the prop
type and the value
type, which prohibits you from assigning prop
and value
types from different members of X
:在该联合的每个元素中,
prop
类型和value
类型之间存在关联,这禁止您从X
的不同成员分配prop
和value
类型:
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
You can also make the compiler calculate this for you programmatically in a few ways, such as building a mapped type that you immediately index into :您还可以通过几种方式让编译器以编程方式为您计算这一点,例如构建一个您立即索引到的映射类型:
type A = { [K in keyof X]-?: {
prop: K;
value: X[K];
} }[keyof X];
It can be verified via IntelliSense that the above definitions of A
are equivalent, but now A
will update automatically if you modify X
.可以通过 IntelliSense 验证
A
的上述定义是等价的,但是现在如果你修改X
, A
会自动更新。
I don't believe you can do this without generics. So here it is:如果没有 generics,我不相信你能做到这一点。所以这里是:
type A<K extends keyof X> = {
prop: K;
value: X[K];
}
The good news is, you only need to specify the generic argument when you're assigning to a variable of an explicitly given type, like so:好消息是,您只需要在分配给显式给定类型的变量时指定泛型参数,如下所示:
const a: A<'a'> = { prop: 'a', value: 'a' };
And it's possible that you will never have this need in your code.并且您的代码中可能永远不会有这种需求。 For example if you specify a function using this generic type, you will not need to explicitly specify the generic parameter to get it working:
例如,如果您使用此通用类型指定 function,则无需显式指定通用参数即可使其正常工作:
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
Your final goal of A is this type.你A的最终目标就是这个类型。 (the same as first answer)
(与第一个答案相同)
type A = {
prop: "a";
value: string;
} | {
prop: "b";
value: number;
} | {
prop: "c";
value: boolean;
};
To make this type,为了制作这种类型,
Define PropValuePairOfX
定义
PropValuePairOfX
type PropValuePairOfX<K extends keyof X> = { prop: K; value: X[K] };
Define PropValuePairMapOfX
.定义
PropValuePairMapOfX
。
type PropValuePairMapOfX = { [K in keyof X]: PropValuePairOfX<K> }; // { // a: {prop: 'a', value: string}, // b: {prop: 'b', value: number}, // c: {prop: 'c', value: boolean} // }
At last, PropValuePairsOfX
to extract PropValuePairMapOfX
's values as UnionType.最后,
PropValuePairsOfX
将PropValuePairMapOfX
的值提取为 UnionType。
// This type is the same as A type PropValuePairsOfX = PropValuePairMap[keyof X]; // {prop: 'a', value: string} | {prop: 'b', value: number} | {prop: 'c', value: boolean} // This type is the same as our goal type const a: PropValuePairsOfX = { prop: 'a', value: 'a' }; // ok const b: PropValuePairsOfX = { prop: 'a', value: 1 }; // not ok, value should be string, because X['a'] is string const c: PropValuePairsOfX = { prop: 'b', value: 1 }; // ok const d: PropValuePairsOfX = { prop: 'c', value: 1 }; // not ok, value should be boolean, because X['c'] is string
(Option) By making X generic argument type, we can make more general type. (选项)通过使 X 成为通用参数类型,我们可以创建更通用的类型。
interface X { a: string; b: number; c: boolean; } type PropValuePair<T, K extends keyof T> = { prop: K; value: T[K] }; type PropValuePairMap<T> = { [K in keyof T]: PropValuePair<T, K> }; type PropValuePairsUnion<T> = PropValuePairMap<T>[keyof T]; const validA: PropValuePairsUnion<X> = { prop: 'a', value: 'a' }; // ok const invalidA: PropValuePairsUnion<X> = { prop: 'a', value: 0 }; // ng
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.