数组的条件类型,在解构时提供错误或泛型

Conditional type for an array to provide eirther an Error or a generic when destructured

在我的 Typescript 项目中,我有一个 return 来自函数的结果类型,包含错误或一些数据。它可以是 [Error, null][null, Data] 的形式之一。例如:

type Result<Data> = [ Error | null, Data | null ]

function randomSuccess(): Result<string> {
    if ( Math.random() > 0.5 ) {
        return [ null, 'success' ]
    else {
        return [ new Error( 'oh no' ), null ]
    }
}

const [ err, result ] = randomSuccess()
if ( err ) {
    .... // can now handle the extracted error and result

我希望 Typescript 检查错误或数据中是否只有一个不为空。例如:

    ...
        return [ new Error( 'oh no' ), 'success' ]

应该会抛出一个错误。

我最初为此编写类型的尝试是使用条件类型:

type Result<Data> = null extends Error ? [ null, Data ] : [ Error, null ]

当函数 return 出现错误时,这可以正常编译。但是,当 returning 有效数据时 - 例如 return [ null, 'success' ] - 编译器会抱怨:

Type 'null' is not assignable to type 'Error'

我想我理解编译器错误:在我的类型定义中 Error 不是参数,因此 null extends Error 将始终为假。但是我不知道从这里去哪里。

我怎样才能创建一个 [Error, null][null, Data] 而不是 [Error, Data] 的类型?

您正在寻找 union type,其中并集的部分是代表两种可能状态的两个元组(数组):

type Result<Data> =
    [ null, Data ]      // Success
    |
    [ Error, null ];    // Failure

A Result<Data> 可以 有一种形式或另一种形式,但不能在两个地方都有 null,也不能有 Error, Data。 (也不能有 2 以外的长度。)

这是一个例子:

type Result<Data> =
    [ null, Data ]      // Success
    |
    [ Error, null ];    // Failure
    
function random(): Result<Date> {
//                        ^^^^−−−− Using `Date` for the `Data` type  argument
//                                just for example
    if (Math.random() < 0.5) {
        // Fail
        return [new Error(), null];
    }
    // Succeed
    return [null, new Date()];
}

const a = random();
if (a[0]) {
    // TypeScript now knows that `a[0]` is an `Error`
    console.log(`Error: ${a[0].message}`);
} else {
    // TypeScript now knows that `a[1]` is a `Date`
    console.log(`Success at ${a[1].toString()}`);
}

Playground link