【发布时间】:2019-11-07 23:12:53
【问题描述】:
我想创建一个行为与in 运算符完全相同的函数,其中使用user-defined type guards 缩小类型。
(例如,请参阅Lodash's has function。)
对于 n in x 表达式,其中 n 是字符串文字或字符串文字类型,x 是联合类型,“true”分支缩小为具有可选或必需属性 n 的类型,“false”分支缩小具有可选或缺失属性 n 的类型。
https://www.typescriptlang.org/docs/handbook/advanced-types.html#using-the-in-operator
我编写了一些测试,展示了 in 运算符的行为以及我们的 has 函数的预期行为。如何定义一个函数 (has) 使其行为与这些测试中的 in 运算符完全相同?
declare const any: any;
type Record = { foo: string; fooOptional?: string };
type Union = { foo: string; fooOptional?: string } | { bar: number; barOptional?: string };
{
const record: Record = any;
if ('foo' in record) {
record; // $ExpectType Record
} else {
record; // $ExpectType never
}
if (has(record, 'foo')) {
record; // $ExpectType Record
} else {
record; // $ExpectType never
}
}
{
const union: Union = any;
if ('foo' in union) {
union; // $ExpectType { foo: string; fooOptional?: string | undefined; }
} else {
union; // $ExpectType { bar: number; barOptional?: string | undefined; }
}
if (has(union, 'foo')) {
union; // $ExpectType { foo: string; fooOptional?: string | undefined; }
} else {
union; // $ExpectType { bar: number; barOptional?: string | undefined; }
}
}
{
const unionWithOptional: { foo: string } | { bar?: number } = any;
if ('bar' in unionWithOptional) {
unionWithOptional; // $ExpectType { bar?: number | undefined; }
} else {
unionWithOptional; // $ExpectType { foo: string; } | { bar?: number | undefined; }
}
if (has(unionWithOptional, 'bar')) {
unionWithOptional; // $ExpectType { bar?: number | undefined; }
} else {
unionWithOptional; // $ExpectType { foo: string; } | { bar?: number | undefined; }
}
}
我最接近解决这个问题的是with this:
type Discriminate<U, K extends PropertyKey> = U extends any
? K extends keyof U
? U
: U & Record<K, unknown>
: never;
export const has = <T extends object, K extends PropertyKey>(
source: T,
property: K,
): source is Discriminate<T, K> =>
property in source;
不幸的是,最后一次测试没有通过。
对于上下文,我想这样做的原因是我的自定义 has 函数可以在编译时验证键(in 运算符不这样做)。我可以弄清楚这部分——我正在努力解决的部分只是模仿in 进行的缩小。
【问题讨论】:
标签: typescript