简体   繁体   English

Typescript generics keyof 类型不匹配

[英]Typescript generics keyof doesn't match type

I have this interface, that just stores a key of another interface ( modelKey ) and the value of that key ( value ):我有这个接口,它只存储另一个接口的键( modelKey )和该键的值( value ):

interface ValueHolder<T, H extends keyof T> {
  modelKey: H;
  value: T[H];
}

Now I want to store the horsePower from the following model, with the matching type in a ValueHolder :现在我想存储来自以下horsePower的马力,匹配类型在ValueHolder中:

interface Car {
  id: number;
  horsePower?: number;
  date: Date;
};

This looks like this:这看起来像这样:

const test: ValueHolder<Car, keyof Car> = {
  modelKey: 'horsePower',
  value: 1000,
};

At this point no error happens and it would store the value fine.此时不会发生错误,它会很好地存储值。 But you could also pass a value of type Date :但您也可以传递Date类型的值:

const test: ValueHolder<Car, keyof Car> = {
  modelKey: 'horsePower',
  value: new Date(),
};

Because for whatever reason the value can accept all types of any key in the model provided:因为无论出于何种原因,该值都可以接受 model 中提供的所有类型的任何键:

(property) ValueHolder<Car, keyof Car>.value: string | number | Date | undefined


How can I make the value key of the interface ValueHolder only accepts values of type undefined | number如何使接口ValueHoldervalue键只接受undefined | number undefined | number , if you provide the modelKey horsePower ? undefined | number ,如果你提供的modelKey horsePower

Demo演示

ValueHolder second generic argument H allows all keys which in turn leads to allowing all values. ValueHolder第二个通用参数H允许所有键,这反过来又导致允许所有值。

You need slightly modify your main utility type to make illegal state unrepresentable.您需要稍微修改您的主要实用程序类型,以使非法 state 无法表示。

Consider this example:考虑这个例子:

type Values<T> = T[keyof T]

type ValueHolder<T> = Values<{
  [Prop in keyof T]: {
    modelKey: Prop;
    value: T[Prop]
  }
}>

// type Test = {
//     modelKey: "id";
//     value: number;
// } | {
//     modelKey: "horsePower";
//     value: number | undefined;
// } | {
//     modelKey: "date";
//     value: Date;
// } | undefined
type Test = ValueHolder<Car>

interface Car {
  id: number;
  horsePower?: number;
  date: Date;
};

// ok
const test: ValueHolder<Car> = {
  modelKey: 'horsePower',
  value: 1000,
};

// error 
const test2: ValueHolder<Car> = {
  modelKey: 'horsePower',
  value: new Date(),
};

Playground操场

See type Test .请参阅type Test ValueHolder creates a union of all allowed values. ValueHolder创建所有允许值的联合。 It iterates through each key and creates this interface {Prop:{modelKey:P, value:T[P]}}.它遍历每个键并创建此接口 {Prop:{modelKey:P, value:T[P]}}。 Then Values obtains each object value {modelKey:P, value:T[P]} and makes a union of them.然后Values获取每个 object 值{modelKey:P, value:T[P]}并将它们合并。

UPDATE更新

Thanks, works fine!谢谢,工作正常! What if I want that T[Prop] can only be of the type string?如果我希望T[Prop]只能是字符串类型怎么办? Is that also possible?这也可能吗?

Yes it is.是的。 There is two ways to achieve it.有两种方法可以实现它。 You can allow ValueHolder only to receive objects where values are strings.您可以只允许ValueHolder接收值为字符串的对象。

type ValueHolder<T extends Record<string, string>> = Values<{
  [Prop in keyof T]: {
    modelKey: Prop;
    value: T[Prop]
  }
}>

Or, you can check inside iteration whether T[Prop] is string or not.或者,您可以在迭代内部检查T[Prop]是否为字符串。

type ValueHolder<T> = Values<{
  [Prop in keyof T]: T[Prop] extends string ? {
    modelKey: Prop;
    value: T[Prop]
  } : never
}>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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