简体   繁体   English

强制使用泛型类型的值的类型安全

[英]Enforce type safety of a value of a generic type

How can i use generics in a way to enforce the type of a value to be of a specific kind? 如何使用泛型将值的类型强制为特定类型?

// An example array
const testArr = [
  {
    id: 3,
    name: 'Spaghetto', // NOTE: Type 'string' here
    shouldNotWork: 3.14, // NOTE: Type 'number' here
  },
  {
    id: 5,
    name: 'Bread',
    shouldNotWork: 3.14,
  },
];

This is what i tried to be my mapping function, but i have to append as V2 to make TS not complain :/ 这就是我试图成为我的映射功能的原因,但是我必须附加as V2才能使TS不抱怨:/

type Mapping<T, U> = (val: T, i: number, arr: T[]) => U;

interface Option<T> {
  value: T;
  label: string; // <- NOTE: Type string is required
}

const typeToOption = <
  T,
  K1 extends keyof T,
  K2 extends keyof T,
  V2 extends T[K2] & string // <- NOTE: 'string' union here to match
>(
  valueK: K1,
  labelK: K2,
): Mapping<T, Option<T[K1]>> => (item: T): Option<T[K1]> => ({
  value: item[valueK],
  label: item[labelK] as V2,
});

I want TS to allow me this 我希望TS允许我这样做

const result = testArr.map(typeToOption('id', 'name'));

...but not this one ...但不是这个

const result = testArr.map(typeToOption('id', 'shouldNotWork'));

How do i make TS complain about the latter? 如何让TS投诉后者?

I think you want to type typeToOption() like this: 我认为您想像这样键入typeToOption()

const typeToOption = <
  T extends Record<K2, string>,
  K1 extends keyof T,
  K2 extends keyof T,
>(
  valueK: K1,
  labelK: K2,
): Mapping<T, Option<T[K1]>> => (item: T): Option<T[K1]> => ({
  value: item[valueK],
  label: item[labelK],
});

TypeScript lets you do some amount "circular" type constraints in generic parameters. 使用TypeScript,您可以在通用参数中执行一些“圆形”类型约束。 So in this case, instead of specifying V2 at all (since it wasn't doing any work), we just require that T extends Record<K2, string> , which means that the type T must have a string valued property at the key K2 . 因此,在这种情况下,我们只需要T extends Record<K2, string> ,而不是完全指定V2 (因为它没有做任何工作),这意味着类型T必须在键处具有string值的属性。 K2 This is enough to give you the behavior you're asking for: 这足以给您您要的行为:

const result = testArr.map(typeToOption('id', 'name')); // okay

const result = testArr.map(typeToOption('id', 'shouldNotWork')); // error
// [ts] Argument of type '"id"' is not assignable to 
// parameter of type '"shouldNotWork"'.

Although the error is a bit bewildering, at least it's an error. 尽管错误有点令人困惑,但至少是错误。 (If it matters: the type inference failure causes funny things to happen. T falls back to Record<'shouldNotWork', string> , equivalent to {shouldNotWork: string} , and keyof T is only 'shouldNotWork' , and therefore K1 extends 'shouldNotWork'`. Oh well.) (如果要紧:类型推断失败会导致有趣的事情发生T退回到Record<'shouldNotWork', string> ,等效于{shouldNotWork: string} ,并且keyof T的键仅是'shouldNotWork' ,因此K1 extends ' shouldNotWork'`。噢。)

Hope that helps; 希望能有所帮助; good luck! 祝好运!

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

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