gpt4 book ai didi

typescript - 为什么 TypeScript 不能从过滤后的数组中推断类型?

转载 作者:行者123 更新时间:2023-12-03 14:14:53 24 4
gpt4 key购买 nike

下面是一些示例代码。 TypeScript 推断 validStudents 的类型如Students[] .任何阅读代码的人都应该清楚,因为所有无效记录都被过滤掉了,validStudents可以安全地认为具有 ValidStudents[] 的类型.

interface Student {
name: string;
isValid: boolean;
}
type ValidStudent = Student & { isValid: true };

const students: Student[] = [
{
name: 'Jane Doe',
isValid: true,
},
{
name: "Robert'); DROP TABLE Students;--",
isValid: false,
}
];

const validStudents = students.filter(student => student.isValid);

function foo(students: ValidStudent[]) {
console.log(students);
}

// the next line throws compile-time errors:
// Argument of type 'Student[]' is not assignable to parameter of type 'ValidStudent[]'.
// Type 'Student' is not assignable to type 'ValidStudent'.
// Type 'Student' is not assignable to type '{ isValid: true; }'.
// Types of property 'isValid' are incompatible.
// Type 'boolean' is not assignable to type 'true'.ts(2345)
foo(validStudents);

可以通过添加类型断言来使此代码工作:

const validStudents = students.filter(student => student.isValid) as ValidStudent[];

......但感觉有点hacky。 (或者也许我只是比我自己更信任编译器!)

有没有更好的方法来处理这个?

最佳答案

这里正在发生一些事情。

第一个(次要)问题是,您的 Student接口(interface),编译器不会检查 isValid属性为 type guard :

const s = students[Math.random() < 0.5 ? 0 : 1];
if (s.isValid) {
foo([s]); // error!
// ~
// Type 'Student' is not assignable to type 'ValidStudent'.
}
如果对象的类型是 discriminated union,编译器只能在检查属性时缩小对象的类型。你正在检查它的判别属性。但是 Student接口(interface)不是联合、歧视或其他;它的 isValid属性是联合类型,但 Student本身不是。
幸运的是,您可以获得一个几乎等效的区分联合版本 Student通过将联合推到顶层:
interface BaseStudent {
name: string;
}
interface ValidStudent extends BaseStudent {
isValid: true;
}
interface InvalidStudent extends BaseStudent {
isValid: false;
}
type Student = ValidStudent | InvalidStudent;
现在编译器将能够使用 control flow analysis了解上述检查:
if (s.isValid) {
foo([s]); // okay
}
此更改并不重要,因为修复它不会突然使编译器能够推断出您的 filter()缩小。但是,如果可以做到这一点,则需要使用诸如可区分联合之类的东西,而不是使用具有联合值属性的接口(interface)。

主要问题是 TypeScript 不会将函数实现内部的控制流分析结果传播到调用函数的范围。
function isValidStudentSad(student: Student) {
return student.isValid;
}

if (isValidStudentSad(s)) {
foo([s]); // error!
// ~
// Type 'Student' is not assignable to type 'ValidStudent'.
}
内部 isValidStudentSad() ,编译器知道 student.isValid表示 studentValidStudent , 但在 isValidStudentSad() 之外,编译器只知道它返回一个 boolean对传入参数的类型没有影响。
处理这种缺乏推理的一种方法是注释这样的 boolean -返回函数为 user-defined type guard function .编译器无法推断它,但您可以断言它:
function isValidStudentHappy(student: Student): student is ValidStudent {
return student.isValid;
}
if (isValidStudentHappy(s)) {
foo([s]); // okay
}
isValidStudentHappy 的返回类型是一个类型谓词, student is ValidStudent .现在编译器将理解 isValidStudentHappy(s)s 的类型有影响.
请注意,已在 microsoft/TypeScript#16069 上提出建议。 ,也许编译器应该能够为 student.isValid 的返回值推断类型谓词返回类型.但是它已经开放了很长时间,我没有看到任何明显的迹象表明它正在努力,所以现在我们不能指望它会被实现。
另请注意,您可以将箭头函数注释为用户定义的类型保护...等价于 isValidStudentHappy是:
const isValidStudentArrow = 
(student: Student): student is Student => student.isValid;

我们快要到了。如果您将回调注释为 filter()作为用户定义的类型守卫,发生了一件奇妙的事情:
const validStudents = 
students.filter((student: Student): student is ValidStudent => student.isValid);

foo(validStudents); // okay!
调用 foo()类型检查!这是因为 TypeScript 标准库类型为 Array.filter()given a new call signature当回调是用户定义的类型保护时,这将缩小数组。万岁!

所以这是编译器可以为你做的最好的事情。缺乏类型保护函数的自动推断意味着在一天结束时您仍然告诉编译器回调会缩小范围,并且并不比您在问题中使用的类型断言更安全。但它更安全一些,也许有一天会自动推断类型谓词。
Playground link to code

关于typescript - 为什么 TypeScript 不能从过滤后的数组中推断类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62032214/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com