【问题标题】:Conditional types in TypeScriptTypeScript 中的条件类型
【发布时间】: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,你是对的,但由于我们的旧代码,我们必须保留它。

标签: javascript typescript types


【解决方案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

【讨论】:

    猜你喜欢
    • 2018-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-10
    • 2019-03-21
    • 1970-01-01
    • 1970-01-01
    • 2018-10-31
    相关资源
    最近更新 更多