基于文字属性值的窄联合类型



我认为我的用例是相当普遍的;我正在尝试键入一个HTTP响应对象,以便只有在状态码恰好为200时才可靠地定义正文。我想返回一个类型为ResponseType的对象,这样的人调用我的函数返回它可以做,例如:

function doSomething<T extends {property: "value"}>(r: ResponseType<T>): void {
if (r.status === 200) {
// r.body has the type 'T & {property: "value"}' here
console.log(r.body.property);
} else {
// r.body has the type 'undefined' here
console.log("no body");
}
}

所以,为了完成这个,我尝试了:

type ResponseType<T> = {
body: T;
status: 200;
} | {
body: undefined;
status: Exclude<number, 200>;
};

但这不起作用,因为number不是联合类型,而且由于number不能赋值给200,但200可以赋值给number,所以这最终相当于

type ResponseType<T> = {
body: T;
status: 200;
} | {
body: undefined;
status: number;
};

并且因为检查number的值不会将其缩小到您要检查的常量(即使您要执行例如if (resp.status === 200 as const)if (resp.status === 200 as 200)),这反过来相当于

// requires --noUnusedParameters to be 'false'
type ResponseType<T> = {
body: undefined;
status: number;
}

因为200number是不同的类型——即使一个可以赋值给另一个——我强烈认为必须有一种方法告诉TypeScript,根据另一个属性的类型推断对象属性的类型是安全的,而不需要调用者编写运行时类型保护检查。但我该怎么做呢?

我想不出其他方法来达到你的目的。这是我的解决方案。

type T200Response<T> = {
body: T;
status: 200;
}
type TNon200Response= {
body: undefined;
status: number;
}
function is200Response<T>(response: TResponse<T>): response is T200Response<T> {
return response.status === 200;
}
type TResponse<T> = T200Response<T> | TNon200Response;
function doSomething<T extends {property: "value"}>(r: TResponse<T>): void {
// is200Response to hints TS which type is it. 
// If this function return true then TS will assume r is T200Response<T> within the if clause
if (is200Response<T>(r)) {
// r.body has the type 'T & {property: "value"}' here
r.body; // Mouseover body you will see T
console.log(r.body.property);
} else {
r.body; // Mouseover body you will see undefined
// r.body has the type 'undefined' here
console.log("no body");
}
}

/C4TwDgpgBAKgTABgQJQgZzAewHZogHhgD4oBeKAbwFgAoKeqAI0wBMQAuWAbloajWABDYAFc0nRAh40AvrVqhIsAHI5JqDDjzlqdBszacR2FhABmAS2wQW0vgOFjO2EQFtGEAE7S5NWmeMAY2ALHCgLNHV0LFwCYgAKT2itCE4YDRi8QiIASk4kzVjwtFgowqziSl4GJNFPbCgCzIgAOgdREtIuqEkfeRpFaHTk2OyyUqQMlLGAHxU1SZG8aX8gkLCWTABlTFcIYAALKwBzQigIAA9gCBMSijBPTEhPUE4AIgA3QQAbEQg3mRERJpKajYh5KAfTAWFhVPT0CxmKDxCJlZrZRI5HJwvh8AD0eMaLQMICgB0EJUO0EGUAA5DAoAAySgPJ5eV5QT4-P4A2lkrwQfq4mrE1ggLhQAlQACymDEEEwHy8TDFUBAcqgAHcLN9vvwIE https://www.typescriptlang.org/play?代码NqsLAlpMN9Wt9MMdEqK2C1Wc9QDk7AwZOdvngccKiSSJVLZfLFcqSWqNdrdfroMZTJZrCwhd6pZ5baTyZSDtTwNBadHzFYbHyM0kjbiTbgzRarfE3thMCq2G9nSXfDIgA

最新更新