TypeScript:如何优化一个可能有属性为 null 的类型
我正在尝试理解如何编写一个函数,该函数可以改进我的类型,然后返回它。
它对传入的对象本身以及函数体内部有效,但在外部无效。此外,我觉得奇怪的是,即使它在函数体内部有效,它在返回语句本身上也会失效。
我的期望是 TypeScript 能正确返回一个精炼/缩小的类型,检查的属性不仅包括主对象。
我哪里做错了?TypeScript 是否需要更多帮助,也许需要一些通用返回类型?
示例:
type Referrer = { id: string }
type User = {
id: string;
referrer: Referrer | null;
} | null;
function checkUserHasReferrer(user: User) {
if (!user) {
throw Error('No User');
}
if (!user.referrer) {
console.log(user.referrer) // referrer correctly narrowed to `referrer: null`
throw Error('No Referrer');
}
const referrer = user.referrer; // referrer correctly narrowed to `referrer: Referrer`
console.log(user.referrer). // same here, `referrer: Referrer`
return user; // <- Here however it's back to `referrer: Referrer | null`
}
let initialUser: User = null;
const user = checkUserHasReferrer(initialUser);
console.log(user.id); // <-- knows User is not `null`
console.log(user.referrer.id); // However it doesn't know referrer is not `null`
TypeScript 游乐场
答案#1
你需要使用断言函数
type Referrer = { id: string }
type User = {
id: string;
referrer: Referrer | null;
}
function notNull(user: unknown): asserts user {
if (!user) {
throw Error('No User');
}
}
declare const user: User | null
notNull(user)
notNull(user.referrer)
console.log(user.id);
console.log(user.referrer.id); // ok
操场
当你需要在 TypeScript 中抛出错误时,通常这样的函数是成为断言函数的好候选
或者,你可以使用自定义类型保护函数
type Referrer = { id: string }
type User = {
id: string;
referrer: Referrer | null;
}
const isValidUser = (user: User | null): user is User & { referrer: Referrer } =>
Boolean(user && user.referrer)
declare const user: User
if (isValidUser(user)) {
console.log(user.id);
console.log(user.referrer.id); // ok
}
操场
答案#2
这是 TypeScript 当前已知的一个限制。参见这里或这里。TypeScript 确实会正确地缩小属性的类型,但父对象的类型不会被修改,因此它仍然只是User。
在不使用类型断言的情况下,一个可能的解决方法是在返回对象时对其进行展开。
return {
...user,
referrer
};
这会将函数的返回类型更改为
{
referrer: Referrer;
id: string;
}
操场
