检索字符串值以给定字符串开头的对象子集
Retrieve subset of object where string values start with given string
给定一个对象
const MyObject = {
a: 'a',
b1: 'b1',
b2: 'b2',
} as const;
我想为一个只包含以字母 "b"
开头的字符串值的对象创建一个类型。我如何根据字符串值的内容过滤对象类型?
这是我要从 typeof MyObject
:
构造的类型
type OnlyBs = {
b1: "b1";
b2: "b2";
}
为了获得所需的解决方案,我使用了 template literal types 和 infer
关键字:
type OnlyBs = {
[K in keyof typeof MyObject]: typeof MyObject[K] extends `b${infer Rest}` ? `b${Rest}` : never;
};
然而,这给了我类型
type OnlyBs = {
readonly a: never;
readonly b1: "b1";
readonly b2: "b2";
}
这接近我想要的,但不是我想要的类型。因此,在这里将它与对象 MyObject2
一起使用会给我类型错误:
const MyObject2: OnlyBs = {
b1: 'b1',
b2: 'b2',
};
"Property 'a' is missing in type '{ b1: "b1"; b2: "b2"; }' but required in type 'OnlyBs'.(2741)"
问题是 TypeScript 要求我添加 a
属性,尽管它的类型是 never
。我想不出另一种方法来实现我的目标。我觉得我很接近,但我无法从生成的对象类型中删除整个 属性(包括它的键)。
顺便说一句: 如果这样做更容易,那么约束对象键(而不是 属性 值)的解决方案也适用于我,因为在这个特定的例子 属性 值等于 属性 键。
TypeScript Playground 我的代码示例
尝试在 OnlyBs
中 return K
而不是 b${Rest}
。这样,只有 Bs returns 个允许的密钥。然后使用Pick获得想要的道具。
考虑这个例子:
const MyObject = {
a: 'a',
b1: 'b1',
b2: 'b2',
} as const;
type AllowedKeys<Obj> = {
[K in keyof Obj]: Obj[K] extends `b${infer _}` ? K : never; // <-- use K instead of `b{infer Rest}`
}[keyof Obj]; // use extra keyof Obj
{
// "b1" | "b2" <-- allowed keys
type _ = AllowedKeys<typeof MyObject>
}
type OnlyBs<Obj> = Pick<Obj, AllowedKeys<Obj>>
{
// readonly b1: "b1";
// readonly b2: "b2";
type _ = OnlyBs<typeof MyObject>
}
const MyObject1: OnlyBs<typeof MyObject> = {
b1: 'b1',
b2: 'b2',
}; // ok
const MyObject2: OnlyBs<typeof MyObject> = {
a: null as any, // expected error
b1: 'b1',
b2: 'b2',
};
如果你想在一种实用程序类型中制作它,请考虑:
type AllowedKeys<Obj> = Pick<Obj, {
[K in keyof Obj]: Obj[K] extends `b${infer _}` ? K : never;
}[keyof Obj]>;
Why does [keyof Obj] at the end of the mapped type AllowedKeys not also retrieve "a"? What's this concept called? Without [keyof Obj] and a call of keyof later, "a" would be there.
让我们删除 [keyof Obj]
:
const MyObject = {
a: 'a',
b1: 'b1',
b2: 'b2',
} as const;
type AllowedKeys<Obj> = {
[K in keyof Obj]: Obj[K] extends `b${infer _}` ? K : never; // <-- use K instead of `b{infer Rest}`
};
{
// {
// readonly a: never;
// readonly b1: "b1";
// readonly b2: "b2";
// }
type _ = AllowedKeys<typeof MyObject>
}
您可能已经注意到,我们正在获取一个对象,如果值存在,则它是允许的键。如果不允许密钥,则为 never
。这就是 a
是 never
的原因。现在。让我们尝试在我们的结果中使用方括号符号。它适用于类型的方式与它适用于普通 js 中的运行时值的方式相同:
{
// {
// readonly a: never;
// readonly b1: "b1";
// readonly b2: "b2";
// }
type a = AllowedKeys<typeof MyObject>['a'] // never
type b = AllowedKeys<typeof MyObject>['b1'] // b1, because keys and allowed values are the same
type ab = AllowedKeys<typeof MyObject>['a' | 'b1'] // still "b1" because any union with never does not make any sense
}
如您所见,ab
键入 returns b1
而不是 b1 | never
。为什么? 你会找到答案的。因此使用 AllowedKeys<typeof MyObject>[keyof typeof MyObject]
重新调整 b1 | b2
因为 TS 编译器删除了 never
.
还有一点,return联合类型不是键的联合,它是对象值的联合。
这里你有一个实用程序类型来获取所有对象值的联合:type Values<T>=T[keyof T]
在 属性 键和 属性 值是相同字符串的情况下约束对象的 键 (而不是值)的解决方案。
这将仅选择键以 "b"
字母开头的属性(MyObject
如问题中所定义):
type OnlyBKeys = {
[K in keyof typeof MyObject as K extends `b${infer _}` ? K : never]: typeof MyObject[K];
};
或任意前置字符串的通用变体:
type KeyStartsWith<Object extends object, Prepend extends string> = {
[K in keyof Object as K extends `${Prepend}${infer _}` ? K : never]: Object[K];
};
这可以用作:
const MyObject3: KeyStartsWith<typeof MyObject, 'b'> = {
b1: 'b1',
b2: 'b2',
};
给定一个对象
const MyObject = {
a: 'a',
b1: 'b1',
b2: 'b2',
} as const;
我想为一个只包含以字母 "b"
开头的字符串值的对象创建一个类型。我如何根据字符串值的内容过滤对象类型?
这是我要从 typeof MyObject
:
type OnlyBs = {
b1: "b1";
b2: "b2";
}
为了获得所需的解决方案,我使用了 template literal types 和 infer
关键字:
type OnlyBs = {
[K in keyof typeof MyObject]: typeof MyObject[K] extends `b${infer Rest}` ? `b${Rest}` : never;
};
然而,这给了我类型
type OnlyBs = {
readonly a: never;
readonly b1: "b1";
readonly b2: "b2";
}
这接近我想要的,但不是我想要的类型。因此,在这里将它与对象 MyObject2
一起使用会给我类型错误:
const MyObject2: OnlyBs = {
b1: 'b1',
b2: 'b2',
};
"Property 'a' is missing in type '{ b1: "b1"; b2: "b2"; }' but required in type 'OnlyBs'.(2741)"
问题是 TypeScript 要求我添加 a
属性,尽管它的类型是 never
。我想不出另一种方法来实现我的目标。我觉得我很接近,但我无法从生成的对象类型中删除整个 属性(包括它的键)。
顺便说一句: 如果这样做更容易,那么约束对象键(而不是 属性 值)的解决方案也适用于我,因为在这个特定的例子 属性 值等于 属性 键。
TypeScript Playground 我的代码示例
尝试在 OnlyBs
中 return K
而不是 b${Rest}
。这样,只有 Bs returns 个允许的密钥。然后使用Pick获得想要的道具。
考虑这个例子:
const MyObject = {
a: 'a',
b1: 'b1',
b2: 'b2',
} as const;
type AllowedKeys<Obj> = {
[K in keyof Obj]: Obj[K] extends `b${infer _}` ? K : never; // <-- use K instead of `b{infer Rest}`
}[keyof Obj]; // use extra keyof Obj
{
// "b1" | "b2" <-- allowed keys
type _ = AllowedKeys<typeof MyObject>
}
type OnlyBs<Obj> = Pick<Obj, AllowedKeys<Obj>>
{
// readonly b1: "b1";
// readonly b2: "b2";
type _ = OnlyBs<typeof MyObject>
}
const MyObject1: OnlyBs<typeof MyObject> = {
b1: 'b1',
b2: 'b2',
}; // ok
const MyObject2: OnlyBs<typeof MyObject> = {
a: null as any, // expected error
b1: 'b1',
b2: 'b2',
};
如果你想在一种实用程序类型中制作它,请考虑:
type AllowedKeys<Obj> = Pick<Obj, {
[K in keyof Obj]: Obj[K] extends `b${infer _}` ? K : never;
}[keyof Obj]>;
Why does [keyof Obj] at the end of the mapped type AllowedKeys not also retrieve "a"? What's this concept called? Without [keyof Obj] and a call of keyof later, "a" would be there.
让我们删除 [keyof Obj]
:
const MyObject = {
a: 'a',
b1: 'b1',
b2: 'b2',
} as const;
type AllowedKeys<Obj> = {
[K in keyof Obj]: Obj[K] extends `b${infer _}` ? K : never; // <-- use K instead of `b{infer Rest}`
};
{
// {
// readonly a: never;
// readonly b1: "b1";
// readonly b2: "b2";
// }
type _ = AllowedKeys<typeof MyObject>
}
您可能已经注意到,我们正在获取一个对象,如果值存在,则它是允许的键。如果不允许密钥,则为 never
。这就是 a
是 never
的原因。现在。让我们尝试在我们的结果中使用方括号符号。它适用于类型的方式与它适用于普通 js 中的运行时值的方式相同:
{
// {
// readonly a: never;
// readonly b1: "b1";
// readonly b2: "b2";
// }
type a = AllowedKeys<typeof MyObject>['a'] // never
type b = AllowedKeys<typeof MyObject>['b1'] // b1, because keys and allowed values are the same
type ab = AllowedKeys<typeof MyObject>['a' | 'b1'] // still "b1" because any union with never does not make any sense
}
如您所见,ab
键入 returns b1
而不是 b1 | never
。为什么? AllowedKeys<typeof MyObject>[keyof typeof MyObject]
重新调整 b1 | b2
因为 TS 编译器删除了 never
.
还有一点,return联合类型不是键的联合,它是对象值的联合。
这里你有一个实用程序类型来获取所有对象值的联合:type Values<T>=T[keyof T]
在 属性 键和 属性 值是相同字符串的情况下约束对象的 键 (而不是值)的解决方案。
这将仅选择键以 "b"
字母开头的属性(MyObject
如问题中所定义):
type OnlyBKeys = {
[K in keyof typeof MyObject as K extends `b${infer _}` ? K : never]: typeof MyObject[K];
};
或任意前置字符串的通用变体:
type KeyStartsWith<Object extends object, Prepend extends string> = {
[K in keyof Object as K extends `${Prepend}${infer _}` ? K : never]: Object[K];
};
这可以用作:
const MyObject3: KeyStartsWith<typeof MyObject, 'b'> = {
b1: 'b1',
b2: 'b2',
};