TypeScript 中的条件类型

Posted

技术标签:

【中文标题】TypeScript 中的条件类型【英文标题】:Conditional types in TypeScript 【发布时间】:2020-03-29 01:02:09 【问题描述】:

我想知道我是否可以在 TypeScript 中使用条件类型?

目前我有如下界面:

interface ValidationResult 
  isValid: boolean;
  errorText?: string;

但我想删除errorText,并且仅当isValidfalse 作为必需属性时才拥有它。

希望我能写成如下界面:

interface ValidationResult 
  isValid: true;


interface ValidationResult 
  isValid: false;
  errorText: string;

但如您所知,这是不可能的。那么,您对这种情况有何看法?

【问题讨论】:

你的意思是,当isValidfalse 时? 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&lt;boolean&gt;)相当于bugs的回答或者CertainPerformance’s answer中产生的union,可以用同样的方式使用。

这里的好处是你也可以传递一个已知的ValidationResult&lt;false&gt; 值,然后你就不必测试isValid,因为它是已知的falseerrorString存在。对于这种情况,可能没有必要——条件类型可能很复杂且难以调试,因此可能不应该不必要地使用它们。但你可以,而且这似乎值得一提。

【讨论】:

我相信这些有时真的很有帮助。但是,对我来说,语法看起来真的很讨厌。 @Peilonrayz 嗯,它与其他Typescript编码的一致性很好,extends是正确使用的运算符。而且它非常强大,特别是因为您还可以使用它来挖掘类型:type SecondOf&lt;T&gt; = T extends Pair&lt;any, infer U&gt; ? U : never; @KRyan 我一直在考虑这个问题。这基本上只是意味着SecondOf&lt;number&gt;“扩展”到Pair&lt;any, number&gt;?我想“不要以封面来判断一本书”这句话在这里很贴切。 @Peilonrayz 啊,不;恰恰相反。 SecondOf&lt;Pair&lt;any, number&gt;&gt; 计算结果为 numberSecondOf&lt;number&gt; 计算为 never,因为 number extends Pair&lt;any, infer U&gt; 为假,因为 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 中的条件类型的主要内容,如果未能解决你的问题,请参考以下文章

聊聊TypeScript类型声明那些最佳实践

聊聊TypeScript类型声明那些最佳实践

聊聊TypeScript类型声明那些最佳实践

TypeScript 中的条件类型

条件类型中的 TypeScript 类型推断

VScode搭建TypeScript开发环境