我之前问过这个问题,试图创建一个类,其中一组键既可以用作参数,也可以使用附加的"!"修改一组键。
因此,我得到了一个类,看起来像这样:export class Schema<PropType extends Record<string, any>> {
properties: Partial<PropType> = {};
constructor(properties: PropType) {
this.properties = properties;
}
/**
* Select specific fields *among those already selected*.
*/
pick = <K extends keyof PropType | `${Extract<keyof PropType, string>}!`>(
...fields: Array<K>
) => {
const newProperties = {} as Record<keyof PropType, unknown>;
fields.forEach((field) => {
const { name, required } = this._parse(field);
newProperties[name] = {
...(this.properties[name]),
required,
};
});
return new Schema<Record<K, unknown>>(
newProperties
);
};
/**
* Parse a field string, returning the base field "name", and
* whether the field is required (if it ends in a !).
*/
_parse = (field: keyof PropType | `${Extract<keyof PropType, string>}!`) => {
return {
name: (field as string).replace("!", "") as keyof PropType,
required: (field as string).split("").reverse()[0] === "!",
};
};
}
pick
函数是奇迹发生的地方。它让我选择'keyName'和'keyName!'如果'keyName'是PropType的密钥之一。在我的情况下,"!"'表示需要密钥。
const Person = new Schema({ firstName: 'Fyodor', middleName: 'Mikhailovich', lastName: 'Dostoevsky' });
const SubPerson = Person.pick('firstName', 'lastName!');
问题是,如果我"pick
调用,它添加感叹号:
SubPerson.pick(
'firstName',
'lastName!',
'lastName!!' // Uh-oh! This shouldn't be acceptable
)
看操场。是否有办法允许${Extract<keyof PropType, string>}!
IFFkeyof PropType
没有"!"?
如果要检测以"!"
结尾的字符串文字类型,可以使用"模式文字类型"在microsoft/TypeScript#40598中实现:
type EndsWithExclamation = `${string}!`
let v: EndsWithExclamation;
v = "okay!" // okay
v = "problem?" // error
所以你可以使用Exclude<T, U>
实用程序类型来过滤keyof T
中以"!"
结尾的字符串:
type QuietKeys<T extends object> = Exclude<keyof T, `${string}!`>;
type X = QuietKeys<{ firstName: unknown, lastName: unknown, "lastName!": unknown }>;
// type X = "firstName" | "lastName"
:
pick = <K extends keyof T | `${Extract<Exclude<keyof T, `${string}!`>, string>}!`>(
...fields: Array<K>
) => {
但是,你可能并不想这样做。pick()
的返回类型为Schema<Record<K, unknown>>
。因此,如果传入像"firstName!"
这样的字段,则输出类型将是Schema<{"firstName!": unknown}>
。但是如果实际的字段名是firstName
,那么您会希望看到Schema<{firstName: unknown}>
。您不希望输出的感叹号。
我这样做的方式是只键K extends keyof T
,这样你就知道K
肯定是原始对象类型的键,而不是添加了!
的键。这样的:
pick = <K extends keyof T>(
...fields: Array<K | `${Extract<K, string>}!`>
) => {
所以现在fields
可以添加感叹号,但K
不能。这给了我们这样的行为:
const SubPerson = Person.pick('firstName', 'lastName!');
// const SubPerson: Schema<Record<"firstName" | "lastName", unknown>>
SubPerson.pick(
'firstName',
'lastName!',
'lastName!!' // error
);
其中lastName!!
被拒绝,不是因为基础键已经以感叹号结束,而是因为基础键只是"lastName"
。
Playground链接到代码