如何在 TS 中使用泛型谓词

问题描述 投票:0回答:1

我想使用数组过滤谓词函数

例如

const isNotEmptyName = <T extends { name: string | null }>(value: T): value is { name: string } => Boolean(value.name);

但是,在这种情况下我有一个错误


A type predicate's type must be assignable to its parameter's type.
  Type '{ name: string; }' is not assignable to type 'T'.
    '{ name: string; }' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ name: string | null; }'.

我做错了什么?

https://www.typescriptlang.org/play?#code/MYewdgzgLgBAlhAciKBRAtgBygT0QQ3QFMYBeGAHgBUYiAPKIsAEwhgG8YxCiAuGaACc4YAOYwAPlwCuAG1kwAvgD4AFADd8s6XxhuAlP03aSCDlx78hI8YrLKAsACgY rt+9cAhECFlF8YBpaOgB03MT6QA

javascript typescript generics types predicate
1个回答
1
投票

你并没有真正做错什么,但是 TypeScript 要求对于 val is Type 形式的

类型谓词
val
的类型必须首先可分配给
Type
,因此
val is Type
是被视为“缩小”。 TypeScript 抱怨 value is {name: string} 的原因是,有人可能会对名称属性比
isNotEmptyName()
窄的值调用
string | null
,例如使用字符串
文字类型
而不是 string
interface Foo {
    name: "hello" | "goodbye" | null;
    age: number;
}
declare const foo: Foo;
isNotEmptyName(foo);

在这种情况下,
{name: string}

被认为是错误的,因为

string
不是
"hello" | "goodbye" | null
的缩小。

最简单的解决方法是从函数中完全删除泛型,因为没有泛型它也会按预期运行:

const isNotEmptyName = (value: { name: string | null }): value is { name: string } => Boolean(value.name)

它可以编译,因为 
{name: string}

{name: string | null}
更窄,当您使用它时,编译器会计算出输入的结果类型应该是什么:
if (isNotEmptyName(foo)) {
    foo // const foo: Foo & { name: string; }
    foo.name.toUpperCase(); // okay
}

您可以看到 
foo

已从

Foo
缩小到
交点
Foo & {name: string},这意味着
foo
必须是这两者。事实上,
foo.name
的类型已缩小为没有
"hello" | "goodbye"
null
,您可以安全地使用
toUpperCase()
取消引用它。

如果由于某种原因你需要保持它的通用性,你可以通过手动编写类型谓词来引用交集来获得相同的效果:

const isNotEmptyName = <T extends { name: string | null }>(value: T): value is T & { name: string } => Boolean(value.name)

可以编译,因为对于任何 
T

(包括

T & U
),
U
绝对可以分配给
{name: string}
。当你调用它时它的作用是一样的:
if (isNotEmptyName(foo)) {
    foo // const foo: Foo & { name: string; }
    foo.name.toUpperCase(); // okay
}

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.