TypeScript 中的条件类型
Posted
技术标签:
【中文标题】TypeScript 中的条件类型【英文标题】:Conditional types in TypeScript 【发布时间】:2020-03-29 01:02:09 【问题描述】:我想知道我是否可以在 TypeScript 中使用条件类型?
目前我有如下界面:
interface ValidationResult
isValid: boolean;
errorText?: string;
但我想删除errorText
,并且仅当isValid
是false
作为必需属性时才拥有它。
希望我能写成如下界面:
interface ValidationResult
isValid: true;
interface ValidationResult
isValid: false;
errorText: string;
但如您所知,这是不可能的。那么,您对这种情况有何看法?
【问题讨论】:
你的意思是,当isValid
是false
时?
isValid 是多余的。你还不如只有errorText,然后如果errorText为null,就没有错误。
是的,@MTilsted,你是对的,但由于我们的旧代码,我们必须保留它。
【参考方案1】:
对这种逻辑建模的一种方法是使用联合类型,类似这样
interface Valid
isValid: true
interface Invalid
isValid: false
errorText: string
type ValidationResult = Valid | Invalid
const validate = (n: number): ValidationResult =>
return n === 4 ? isValid: true : isValid: false, errorText: "num is not 4"
然后编译器能够根据布尔标志缩小类型
const getErrorTextIfPresent = (r: ValidationResult): string | null =>
return r.isValid ? null : r.errorText
【讨论】:
不错的答案。令人着迷的是,编译器实际上可以在这里判断r
必须是 Invalid
类型。
这称为区分联合。很酷的东西:typescriptlang.org/docs/handbook/…【参考方案2】:
为避免创建多个仅习惯于创建第三个接口的接口,您也可以直接替换为type
:
type ValidationResult =
isValid: false;
errorText: string;
|
isValid: true;
;
【讨论】:
这个语法叫什么?我想详细了解它的作用和工作原理,但我什至不知道要查找什么!【参考方案3】:union demonstrated by bugs 是我推荐的处理方式。尽管如此,Typescript确实有一个名为“conditional types”的东西,他们可以处理这个问题。
type ValidationResult<IsValid extends boolean = boolean> = (IsValid extends true
? isValid: IsValid;
: isValid: IsValid; errorText: string;
);
declare const validation: ValidationResult;
if (!validation.isValid)
validation.errorText;
这个ValidationResult
(由于默认参数实际上是ValidationResult<boolean>
)相当于bugs的回答或者CertainPerformance’s answer中产生的union,可以用同样的方式使用。
这里的好处是你也可以传递一个已知的ValidationResult<false>
值,然后你就不必测试isValid
,因为它是已知的false
和errorString
存在。对于这种情况,可能没有必要——条件类型可能很复杂且难以调试,因此可能不应该不必要地使用它们。但你可以,而且这似乎值得一提。
【讨论】:
我相信这些有时真的很有帮助。但是,对我来说,语法看起来真的很讨厌。 @Peilonrayz 嗯,它与其他Typescript编码的一致性很好,extends
是正确使用的运算符。而且它非常强大,特别是因为您还可以使用它来挖掘类型:type SecondOf<T> = T extends Pair<any, infer U> ? U : never;
。
@KRyan 我一直在考虑这个问题。这基本上只是意味着SecondOf<number>
“扩展”到Pair<any, number>
?我想“不要以封面来判断一本书”这句话在这里很贴切。
@Peilonrayz 啊,不;恰恰相反。 SecondOf<Pair<any, number>>
计算结果为 number
。 SecondOf<number>
计算为 never
,因为 number extends Pair<any, infer U>
为假,因为 number
没有扩展任何 Pair
【参考方案4】:
这是一种不需要 isValid 属性的替代方法。相反,我们可以使用 errortext
属性的存在或不存在作为标记。这是一个例子:
// Empty for now, can always add properties to it
interface Valid
interface InValid
errorText: string;
// sum/union type, the type is either Valid OR InValid
type ValidationResult = Valid | InValid;
// custom type guard to determine the type of the result
// TS uses this function to narrow down the type to eiter valid or invalid
function checkIfValidResult(result: ValidationResult): result is InValid
return result.hasOwnProperty('errorText') ? true : false;
// example of using the type guard
function doSomethingWithResult(result: ValidationResult)
if (checkIfValidResult(result))
throw new Error(result.errorText);
else
console.log('Success!');
doSomethingWithResult();
// logs: Success
doSomethingWithResult(errorText:'Oops something went wrong');
// Throws error: Oops something went wrong
【讨论】:
以上是关于TypeScript 中的条件类型的主要内容,如果未能解决你的问题,请参考以下文章