简体   繁体   English

TypeScript 具有详尽枚举检查的接口类型

[英]TypeScript Interface typing with exhaustive enum check

I have a enum and I let each value of the enum as a key in my interface whose value is specific to the key.我有一个枚举,我让枚举的每个值作为我的接口中的键,其值特定于键。

Example:例子:

enum Fruits {
  Apple = "Apple",
  Banana = "Banana",
}

// EDIT: These slicers can have completely different shapes.
interface AppleSlicer { foo: () => string }
interface BananaSlicer { bar: () => number }

interface FruitSlicers {
  [Fruits.Apple]: AppleSlicer,
  [Fruits.Banana]: BananSlicer,
}

This works well, but I want have similar code in several places and I want them to give me compile errors when there's a new entry in the enum.这很好用,但我希望在几个地方有类似的代码,并且我希望它们在枚举中有新条目时给我编译错误。 Currently, this doesn't do any exhaustive check, so it does not.目前,这并没有做任何详尽的检查,所以它没有。 Is it possible to achieve that with TypeScript?是否可以使用 TypeScript 来实现?

For this you will need type compatibility checks.为此,您将需要类型兼容性检查。 My recommended library for this is ts-expect : https://github.com/TypeStrong/ts-expect我为此推荐的库是ts-expecthttps://github.com/TypeStrong/ts-expect

Here is a complete sample (I've inlined expectType and TypeOf from ts-expect):这是一个完整的示例(我已经从 ts-expect 内联expectTypeTypeOf ):

export const expectType = <Type>(value: Type): void => void 0;
export type TypeOf<Target, Value> = Exclude<Value, Target> extends never
  ? true
  : false;

type AppleSlicer = { apples: number }
type BananaSlicer = { bananas: number }

enum Fruits {
  Apple = "Apple",
  Banana = "Banana",
}

type FruitSlicersComplete = {
  [Fruits.Apple]: AppleSlicer,
  [Fruits.Banana]: BananaSlicer,
}
type FruitSlicersIncomplete = {
  [Fruits.Apple]: AppleSlicer,
  // [Fruits.Banana]: BananaSlicer,
}

type FruitSlicersExhaustive = {[key in Fruits]: any}

expectType<TypeOf<FruitSlicersExhaustive,FruitSlicersComplete>>(true); // Success ✅
expectType<TypeOf<FruitSlicersExhaustive,FruitSlicersIncomplete>>(true); // Error ❌

You probably want to make use of the Record utility type , which works great with enums.您可能想要使用Record实用程序类型,它对枚举非常有用。

Something like:就像是:

enum Fruits {
  Apple = 'Apple',
  Banana = 'Banana',
}

type Slicer = Function; // Quick example case

type FruitSlicers = Record<Fruits, Slicer>; // <- Ensures every enum value is a key

const fruitSlicers: FruitSlicers = {
  [Fruits.Apple]: () => {},
  [Fruits.Banana]: () => {},
};

Now if I add another Fruit :现在,如果我添加另一个Fruit

enum Fruits {
  Apple = 'Apple',
  Banana = 'Banana',
  Cherry = 'Cherry',
}

I get:我得到:

Property 'Cherry' is missing in type '{ Apple: () => void; Banana: () => void; }' but required in type 'FruitSlicers'

You can use an identity function to constrain the input parameter type so that that it must include all of the keys in the string enum, and also all of the entries in the interface:您可以使用标识 function来约束输入参数类型,以便它必须包含字符串枚举中的所有键,以及接口中的所有条目:

TS Playground TS游乐场

enum Fruit {
  Apple = 'Apple',
  Banana = 'Banana',
  Orange = 'Orange',
}

interface AppleSlicer { foo: () => string }
interface BananaSlicer { bar: () => number }

interface FruitSlicers {
  [Fruit.Apple]: AppleSlicer;
  [Fruit.Banana]: BananaSlicer;
}

function createSlicers <T extends Record<Fruit, unknown> & FruitSlicers>(value: T): T {
  return value;
}

const slicers1 = createSlicers({
  Apple: { foo: () => 'apple' },
  Banana: { bar: () => 1 },
}); // Error (2345)

const slicers2 = createSlicers({
  Apple: { foo: () => 'apple' },
  Banana: { bar: () => 1 },
  Orange: 'not in the interface, so can be any value',
}); // ok

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

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