TypeScript:从 Enum 键入接口,可选但至少 1
TypeScript: Key in interface from Enum, Optional but at least 1
我想为具有 userId 或 customerId 的对象创建一个 接口 。
要么一个,一个。
key 应该由枚举定义(因此命名总是相同的)。
我是这样用的,来自另一个例子:
export enum EId {
userId = 'userId',
customerId = 'customerId',
}
export type IIdParam = {
[key in EId]: string;
};
export interface IIdParamString extends IIdParam {
paramString: string;
}
最后我想要这个对象:
const params: IIdParamString = {
userId: String('1234'),
paramString: 'limit',
};
但我得到一个错误:当 [key in EId]
不是可选的时,它会抱怨它需要 userId 和 customerId,如果不是可选的,则...
也许这不是我想要的正确方法。
我想枚举中的值实际上不需要密钥,但我不能更好地从示例中抽象它。
考虑这个例子:
export enum EId {
userId = 'userId',
customerId = 'customerId',
}
type AtLeastOne<Obj, Keys = keyof Obj> = Keys extends keyof Obj ? Record<Keys, string> : never
// credits goes to
type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> =
T extends any
? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>
export type IIdParamString =
& StrictUnion<AtLeastOne<typeof EId>>
& {
paramString: string;
}
/**
* Ok
*/
const params: IIdParamString = {
userId: '1234',
paramString: 'limit',
};
/**
* Expected errors
*/
const params2: IIdParamString = {
paramString: 'limit',
};
const params3: IIdParamString = {
userId: '1234',
customerId: 'sdf',
paramString: 'limit',
};
AtLeastOne
- 你可以找到完整的解释
StrictUnion
- 你可以找到完整的解释 。此实用程序与您所做的 never
有相似的技巧。
Is my approach in general conventional to my problem, to make sure it is either this or that
是的,你的方法没问题。但我建议你使用有区别的工会。它们也被称为 tagged unions
。例如参见 [=21=]。您可能已经注意到,每个工会都有自己的 tag/flag/marker。它有助于编译器区分它们。
Could it be an interface ?
Unfortunately - no. It could be only type, because interface
can extends only statically known type. See example:
// An interface can only extend an object type or intersection of object types with statically known members.
export interface IIdParamString extends StrictUnion<AtLeastOne<typeof EId>> { // error
paramString: string;
}
关于你最后一个问题,我不确定我是否理解。
您可以更新您的问题或(更好的)选项以单独询问。
我想为具有 userId 或 customerId 的对象创建一个 接口 。 要么一个,一个。
key 应该由枚举定义(因此命名总是相同的)。
我是这样用的,来自另一个例子:
export enum EId {
userId = 'userId',
customerId = 'customerId',
}
export type IIdParam = {
[key in EId]: string;
};
export interface IIdParamString extends IIdParam {
paramString: string;
}
最后我想要这个对象:
const params: IIdParamString = {
userId: String('1234'),
paramString: 'limit',
};
但我得到一个错误:当 [key in EId]
不是可选的时,它会抱怨它需要 userId 和 customerId,如果不是可选的,则...
也许这不是我想要的正确方法。 我想枚举中的值实际上不需要密钥,但我不能更好地从示例中抽象它。
考虑这个例子:
export enum EId {
userId = 'userId',
customerId = 'customerId',
}
type AtLeastOne<Obj, Keys = keyof Obj> = Keys extends keyof Obj ? Record<Keys, string> : never
// credits goes to
type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> =
T extends any
? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>
export type IIdParamString =
& StrictUnion<AtLeastOne<typeof EId>>
& {
paramString: string;
}
/**
* Ok
*/
const params: IIdParamString = {
userId: '1234',
paramString: 'limit',
};
/**
* Expected errors
*/
const params2: IIdParamString = {
paramString: 'limit',
};
const params3: IIdParamString = {
userId: '1234',
customerId: 'sdf',
paramString: 'limit',
};
AtLeastOne
- 你可以找到完整的解释
StrictUnion
- 你可以找到完整的解释 never
有相似的技巧。
Is my approach in general conventional to my problem, to make sure it is either this or that
是的,你的方法没问题。但我建议你使用有区别的工会。它们也被称为 tagged unions
。例如参见 [=21=]。您可能已经注意到,每个工会都有自己的 tag/flag/marker。它有助于编译器区分它们。
Could it be an interface ? Unfortunately - no. It could be only type, because
interface
can extends only statically known type. See example:
// An interface can only extend an object type or intersection of object types with statically known members.
export interface IIdParamString extends StrictUnion<AtLeastOne<typeof EId>> { // error
paramString: string;
}
关于你最后一个问题,我不确定我是否理解。 您可以更新您的问题或(更好的)选项以单独询问。