TypeScript联合类型缩小和分配问题

时间:2020-03-15 14:32:12

标签: typescript types

TypeScript可以使用以下示例代码正确缩小联合类型:

interface Callable {
  call(): void
}
declare function getCallable(): Callable

let x: string | Callable = { call(){} }

if (typeof x === 'string') {
  x = getCallable()
  // `x` can only be `Callable` here
}
x.call()

但是它在实际代码上不起作用,类似于上面的代码:

form(obj: object | FormData): Request {
    if (!(obj instanceof FormData)) {
        const form = new FormData()
        for (const [k, v] of Object.entries(obj)) {
            form.append(k, v)
        }
        obj = form
    }
    // `obj` must be FormData here, but why `obj` is still `object | FormData`?
    const stream = new Readable()
    stream.push(obj.getBuffer()) // FormData has getBuffer() and getHeaders()
    this._body = stream
    this.header(obj.getHeaders())
    return this
}

我认为!(obj instanceof FormData)意味着只有object可以进入if块,并且obj在那里被分配给FormData,所以obj必须为FormData。 但是TypeScript编译器抱怨obj没有getBuffer()getHeaders(),因为它们是object | FormData,而不是FormData。另外,如果不直接像(<FormData>obj).getBuffer()一样投射怎么办?

1 个答案:

答案 0 :(得分:2)

问题与TS如何处理参数有关。它们被视为const,而不是let。这意味着TS不会遵循参数的重新分配。当然,您可以这样做,因为在运行时有可能(不重新分配const),但是TS处于参数的“ const模式”。解决方案是临时变量:

function form(obj: object | FormData): Request {
    let finalObj: FormData;
    if (!(obj instanceof FormData)) {
        const form = new FormData()
        for (const [k, v] of Object.entries(obj)) {
            form.append(k, v)
        }
        finalObj = form
    } else {
      finalObj = obj;
    }
    // rest of the code should use finalObj

为什么TS会这样做-一般而言,重新分配参数不是一个好习惯,因此将其视为const是合理的。看看与该行为完全相同的类似问题-here