简体   繁体   English

打字稿:简化枚举类对象的输入

[英]Typescript: Simplify typing of enum-like objects

in JavaScript I often (ab)use objects as pseudo-enums :在 JavaScript 中,我经常(ab)使用对象作为伪枚举

const application = {
    ELECTRIC: {propA: true, propB: 11, propC: "eee"},
    HYDRAULIC: {propA: false, propB: 59, propC: "hhh"},
    PNEUMATIC: {propA: true, propB: 87, propC: "ppp"},
}

const foo = application.ELECTRIC
const bar = application.HYDRAULIC

I'd like to achieve a similar behavior in TypeScript, but in a type-safe manner.我想在 TypeScript 中实现类似的行为,但以类型安全的方式。 Here's what I have so far:这是我到目前为止所拥有的:

type Application = "ELECTRIC" | "HYDRAULIC" | "PNEUMATIC";

interface ApplicationProps {
  propA: boolean;
  propB: number;
  propC: string;
}

const application: Record<Application, ApplicationProps> = {
    ELECTRIC: {propA: true, propB: 11, propC: "eee"},
    HYDRAULIC: {propA: false, propB: 59, propC: "hhh"},
    PNEUMATIC: {propA: true, propB: 87, propC: "ppp"},
}

const foo = application.ELECTRIC;
---
let bar: ApplicationProps; // an attempt to restrict bar to application.ELECTRIC|application.HYDRAULIC|application.PNEUMATIC
bar = application.HYDRAULIC;

That works okay but feels clumsy.这行得通,但感觉很笨拙。

1) There seems to be lot of repetition in type/interface/object 1)类型/接口/对象似乎有很多重复

Is there some TS-Trickery to automatically infer type Application / interface ApplicationProps from the object?是否有一些 TS-Trickery 可以从对象自动推断type Application / interface ApplicationProps Like some keyof typeof something magic?像一些keyof typeof something

2) It feels strange to type bar as ApplicationProps when I actually want to restrict it to members of application 2)当我实际上想将它限制为application成员时,将bar键入为ApplicationProps感觉很奇怪

Is there a more elegant way to restrict bar to application.ELECTRIC|application.HYDRAULIC|... ?有没有更优雅的方式将bar限制为application.ELECTRIC|application.HYDRAULIC|...

3) If I want to define bar in another file, I need to import ApplicationProps and application . 3)如果我想在另一个文件中定义bar ,我需要导入ApplicationPropsapplication I know the type info is part of application but I don't know how to get it.我知道类型信息是application的一部分,但我不知道如何获取它。

The best I got so far was: let bar: typeof application[keyof typeof application] Which of course is stupid.到目前为止我得到的最好的是: let bar: typeof application[keyof typeof application]这当然是愚蠢的。 There has to be a better way?!一定有更好的方法?!



Thanks in advance :) 提前致谢 :)

So you can do something like this:所以你可以做这样的事情:


// all application keys will be defined here
// as const is needed here so typescript won't start widening the type to string[]
const applications = ["ELECTRIC", "HYDRAULIC", "PNEUMATIC"] as const

// get all keys as a Union Type
type ApplicationKeys = typeof applications[number]
// create your enum object as a type
type Application = { [Key in ApplicationKeys]: ApplicationProps<Key> }
// define your props with a key so typescript can differentiate between your enums
type ApplicationProps<T> = {
  key: T,
  propA: boolean;
  propB: number;
  propC: string;
}
// the actual enum definition
const application: Application = {
  ELECTRIC: { key: "ELECTRIC", propA: true, propB: 11, propC: "eee" },
  HYDRAULIC: { key: "HYDRAULIC", propA: false, propB: 59, propC: "hhh" },
  PNEUMATIC: { key: "PNEUMATIC", propA: true, propB: 87, propC: "ppp" },
}
// test
let foo = application.ELECTRIC;
let bar: Application["PNEUMATIC"]; 
bar = application.HYDRAULIC; // error: Type 'ApplicationProps<"HYDRAULIC">' is not assignable to type 'ApplicationProps<"ELECTRIC">'. 
bar ===foo // error his condition will always return 'false' since the types have no overlap

The neat part is that if you want to add a new application key you just have to add it to the list and typescript will throw errors for the missing or unhandled key in a switch or if statement(exhaustive switch block)[https://stackoverflow.com/questions/39419170/how-do-i-check-that-a-switch-block-is-exhaustive-in-typescript].巧妙的部分是,如果您想添加一个新的应用程序密钥,您只需将其添加到列表中,打字稿将针对 switch 或 if 语句中丢失或未处理的密钥引发错误(详尽的 switch 块)[https:// stackoverflow.com/questions/39419170/how-do-i-check-that-a-switch-block-is-exhaustive-in-typescript]。

I hope you understand my bad english :) CODE我希望你能理解我糟糕的英语:) 代码

I sometimes do something like this, with having a enum list mapping to specific types.我有时会做这样的事情,将枚举列表映射到特定类型。

But I'm also curious about how this could be done better.但我也很好奇如何才能做得更好。

enum ApplicationType {
  ELECTRIC = 'ELECTRIC',
  HYDRAULIC = 'HYDRAULIC',
  PNEUMATIC = 'PNEUMATIC',
}

interface Application {
  propA: boolean;
  propB: number;
  propC: string;
}

interface ElectricApplication extends Application {
  propD: string;
}
interface PneumaticApplication extends Application {}
interface HydaulicApplication extends Application {}

type ApplicationTypeMap = {
  [ApplicationType.ELECTRIC]: ElectricApplication;
  [ApplicationType.HYDRAULIC]: HydaulicApplication;
  [ApplicationType.PNEUMATIC]: PneumaticApplication;
};

const applications: { [key in ApplicationType]: ApplicationTypeMap[key] } = {
  ELECTRIC: {
    propA: true,
    propB: 11,
    propC: 'eee',
    propD: 'boo',
  },
  HYDRAULIC: { propA: false, propB: 59, propC: 'hhh' },
  PNEUMATIC: { propA: true, propB: 87, propC: 'ppp' },
};

const foo = applications[ApplicationType.ELECTRIC];
console.log(foo.propD);

const bar = applications[ApplicationType.HYDRAULIC];
console.log(bar.propA);

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

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