使用枚举作为Typescript中的受限键类型

t9aqgxwy  于 2023-01-21  发布在  TypeScript
关注(0)|答案(5)|浏览(174)

enum是否可以用作keytype,而不仅仅是numberstring?目前似乎唯一可能的声明是x:{[key:number]:any},其中key可以是numberstring类型。是否可以在此示例中进行如下操作:
示例:

enum MyEnum
{
    First,
    Second
}

var layer:{[key:MyEnum]:any};
yyyllmsg

yyyllmsg1#

Since 2018,在Typescript中有一种更简单的方法,无需使用keyof typeof

let obj: { [key in MyEnum]: any} =
 { [MyEnum.First]: 1, [MyEnum.Second]: 2 };

要不必包括所有密钥:

let obj: { [key in MyEnum]?: any} =
 { [MyEnum.First]: 1 };

in Enumkeyof typeof Enum

本节将详细介绍in Enumkeyof typeof之间的区别,以及为什么keyof typeof可能是一个不好的选择。
主要的区别是in Enum编译为枚举 * 值 *,而keyof typeof编译为枚举 * 键 *,但是您应该注意一些更微妙的功能。
对于keyof typeof,您不能在以后修改枚举属性:

let obj: { [key in keyof typeof MyEnum]?: any} = { First: 1 };
obj.First = 1;
// Cannot assign to 'First' because it is a read-only property.

...除非您使用-readonly

let obj: { -readonly [key in keyof typeof MyEnum]?: any} = { First: 1 };
obj.First = 1; // works

但是您可以使用任何其他整数键就好了!?

let obj: { [key in keyof typeof MyEnum]?: any} = { First: 1 };
obj[2] = 1;

这是因为keyof typeof将编译为:

{
    [x: number]: any;
    readonly First?: any;
    readonly Second?: any;
}

注意[x: number]readonly属性的存在。如果使用字符串枚举,则[x: number]属性存在。
相反,使用in Enum,可以修改对象:

enum MyEnum {
    First,  // default value of this is 0
    Second, // default value of this is 1
}

let obj: { [key in  MyEnum]?: any} = { [MyEnum.First]: 1 };
obj[MyEnum.First] = 1; // can use the enum...
obj[0] = 1;            // but can also use the enum value, 
                       // as it is a numeric enum by default

注意我们是如何直接使用枚举值的:这是一个数字枚举。这并不意味着我们可以使用任何整数键:

obj[42] = 1;
// Element implicitly has an 'any' type because 
// expression of type '42' can't be used to index type '{ 0?: any; 1?: any; }'.
// Property '42' does not exist on type '{ 0?: any; 1?: any; }'.

如果我们看看类型,这就很明显了:

{
    0?: any;
    1?: any;
}

只有01,我们的枚举值是允许的,其他值是禁止的。这更符合你对枚举工作的期望,不像keyof typeof,没有什么令人惊讶的。
当然,这也适用于字符串和异构枚举:

enum MyEnum
{
    First = 1,
    Second = "YES"
}

let obj: { [key in  MyEnum]?: any} = { [MyEnum.First]: 1, [MyEnum.Second]: 2 };
obj[1] = 0;
obj["YES"] = 0;

这里的类型是:

{
    1?: any;
    YES?: any;
}

您可以使用readonly按预期选择不变性:

let obj: { readonly [key in MyEnum]?: any} = { 
    [MyEnum.First]: 1,
};
obj[MyEnum.First] = 2;
// Cannot assign to '1' because it is a read-only property.

...这使得所有枚举键readonly

{
    readonly 1?: any;
    readonly 2?: any;
}

自动递增陷阱

小心自动增量!

enum MyEnum
{
    First = 1,
    Second,
    Third = 2,
}

let obj: { [key in  MyEnum]?: any} = { 
    [MyEnum.First]: 10, 
    [MyEnum.Second]: 20,
    [MyEnum.Third]: 30,
};
// {1: 10, 2: 30}

20值消失了!在TS中,数字枚举是自动递增的,但不考虑重复的值。这意味着MyEnum.SecondMyEnum.Third的值是2! TS没有警告这一点,所以请确保在可能的情况下lint它。

总结

| in Enum|keyof typeof Enum|
| - ------|- ------|
| 编译为枚举|编译为枚举|
| * * 不允许枚举之外的值**|如果使用数值枚举,则可以允许枚举之外的数值|
| * * 可以修改对象,不可变选择readonly**|如果没有-readonly,则无法修改枚举属性。枚举之外的其他数值可以是|
| 枚举值可能会因为自动递增而发生冲突|枚举值可能会因为自动递增而发生冲突|
如果可能,请使用in Enum。如果您的代码库使用keyof typeof Enum,请注意这些陷阱。

9fkzdhlc

9fkzdhlc2#

是的。只要输入

let layer:{[key in keyof typeof MyEnum]: any}

从Typescript 2.1开始就可以使用keyof关键字。更多细节请参见TypeScript documentation。仅使用keyof作为枚举将不起作用(您将获得enum类型的键,而不是枚举常量),因此您必须键入keyof typeof

vaj7vani

vaj7vani3#

简短回答:

let layer: Partial<Record<MyEnum, unknown>>;

详细答案(包含详细信息):

我也遇到了同样的问题。我想让下面的代码工作。

enum MyEnum {
    xxx = "xxx",
    yyy = "yyy",
    zzz = "zzz",
}

type myType = ...; // Fill here

const o: myType = { // keys should be in MyEnum, values: number
   [MyEnum.xxx]: 2,
   "hi": 5, // <--- Error: complain on this one, as "hi" is not in enum
}

o[MyEnum.yyy] = 8; // Allow it to be modified later

other answer开始讨论这个问题,我得到:

type myType = {[key in keyof typeof MyEnum]: number};

但是这会让o is missing "yyy"很麻烦,所以我需要告诉它这个对象会有一些枚举键,而不是所有的,所以我需要加上?来得到:

type myType = {[key in keyof typeof MyEnum]?: number};

它一直工作得很好,直到我在代码末尾添加了一行代码,以便在对象第一次创建后对其进行修改。现在它抱怨说,通过keyof继承的类型具有readonly属性,并且在第一次创建后我无法触及这些属性!|实际上,将鼠标悬停在Typescript Playground中的myType上,将显示为:

type myType = {
    readonly xxx?: number | undefined;
    readonly yyy?: number | undefined;
    readonly zzz?: number | undefined;
}

现在,要删除不需要的readonly,我发现可以使用:

type myType = {-readonly [key in keyof typeof myEnum1]?: number };
    • 很丑,但很管用**!

直到我玩过Typescript实用程序类型,并找到了我想要的!

type myType = Partial<Record<keyof typeof MyEnum, number>>;

:)

7gyucuyw

7gyucuyw4#

对于那些正在寻找一种方法来获得enumkeys而不是它的value的人来说,你只需要从这个改变:

type myType = Partial<Record<MyEnum, number>>;

改为:

type myType = Partial<Record<keyof typeof MyEnum, number>>;
92dk7w1h

92dk7w1h5#

事实上,它对我很有效,没有任何变通办法。
以下是一些案例:

enum EnumString {
  a="a",
  b="b"
}

enum EnumNum {
  a,
  b
}

type Type = "a" | "b"

let x1:{[k in EnumString]?:any }={a:1} //working
let x2:{[k in EnumNum]?:any }={a:1} //error: Type '{ a: number; }' is not assignable to type '{ 0?: any; 1?: any; }'
let x3:{[k in Type]?:any }={a:1} //working; defining a union type is easer than defining an enum string 

let y1:{[k: EnumString]?:any }={a:1}
let y2:{[k: EnumNum]?:any }={a:1}
let y3:{[k in Type]?:any }={a:1}

let z1:{[k in keyof typeof EnumString]?:any }={a:1}
let z2:{[k in keyof typeof EnumNum]?:any }={a:1}
let z3:{[k: keyof typeof Type]?:any }={a:1}

重要的是要注意到,“z1”的情况下,即使它工作得很好,但你不能修改它以后,因为它是一个只读。你可以使用关键字-readonly来删除这个约束。

x1.a=2 
x3.a=2
z1.a=2 //error: read only

我创建了一个 typescript Playground,让您自己查看不同案例的问题

相关问题