类中字符串的模板字面值,如果字符串匹配某些特征



我之前问过这个问题,试图创建一个类,其中一组键既可以用作参数,也可以使用附加的"!"修改一组键。

因此,我得到了一个类,看起来像这样:
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链接到代码

最新更新