简体   繁体   English

Typescript:相同object的其他属性参考属性类型

[英]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的不同成员分配propvalue类型:

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的上述定义是等价的,但是现在如果你修改XA会自动更新。

Playground link to code 游乐场代码链接

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

Playground link 游乐场链接

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,为了制作这种类型,

  1. Define PropValuePairOfX定义PropValuePairOfX

     type PropValuePairOfX<K extends keyof X> = { prop: K; value: X[K] };
  2. 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} // }
  3. At last, PropValuePairsOfX to extract PropValuePairMapOfX 's values as UnionType.最后, PropValuePairsOfXPropValuePairMapOfX的值提取为 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
  4. (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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM