TypeScript 中的条件类型

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

目前我有以下界面:

interface ValidationResult {
isValid: boolean;
errorText?: string;
}

但是我想删除 errorText,并且只有当 isValidfalse作为 需要属性时才使用它。

我希望我能把它写成如下界面:

interface ValidationResult {
isValid: true;
}


interface ValidationResult {
isValid: false;
errorText: string;
}

但是你知道,这是不可能的。那么,你对这种情况有什么看法?

9690 次浏览

建立这种逻辑模型的一种方法是使用联合类型,类似这样的

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
}

为了避免创建多个仅用于创建第三个接口的接口,您还可以直接使用 type替代:

type ValidationResult = {
isValid: false;
errorText: string;
} | {
isValid: true;
};

我推荐使用 由错误演示的联合来处理这个问题。尽管如此,类型脚本 是的有一个称为“ 条件类型”的东西,它们可以处理这个问题。

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>)等价于 bug 答案或 肯定性能的答案中产生的联合,并且可以以相同的方式使用。

这里的优点是您也可以传递一个已知的 ValidationResult<false>值,然后您就不必测试 isValid,因为它将是已知的 false,并且 errorString将是已知的存在。对于这样的情况可能没有必要ーー而且条件类型可能很复杂并且难以调试,因此可能不应该不必要地使用它们。但是你可以,这似乎值得一提。

这里有一种不需要 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