为什么我在 TypeScript 中的对象解构中收到“不安全的数组解构”?

问题描述 投票:0回答:1

我在 TypeScript 中遇到 IteratorResult 解构问题。我正在做一个简单的对象解构,但遇到了数组解构错误。

这是引发 TypeScript 错误的代码:

class ServiceLabelCollection extends ICollection<ServiceLabel> {
    private collection: Collection<ServiceLabel>;
    
    public addSoft(labels: ServiceLabel | Iterable<ServiceLabel>): this {
        const isIterable = !(labels instanceof ServiceLabel);
        const toAddIterable: Iterable<ServiceLabel> = isIterable ? labels : [labels];
        const iterator: Iterator<ServiceLabel> = toAddIterable[Symbol.iterator]();
    
        while (this.collection.size < ServiceLabelsCollection.maxSize) {
            const { value, done }: IteratorResult<ServiceLabel> = iterator.next(); // Unsafe array destructuring of a tuple element with an `any` value. eslint@typescript-eslint/no-unsafe-assignment
            
            if (done) break;
            this.collection.addSoft(value);
        }
    
        return this;
    }
}

这个问题似乎是一个错误,因为它是一个对象解构,而且我认为它实现得很好。我认为这可能是 TypeScript bug 的另一个原因是下一个代码没有给出错误:

class ServiceLabelCollection extends ICollection<ServiceLabel> {
    private collection: Collection<ServiceLabel>;
    
    public addSoft(labels: ServiceLabel | Iterable<ServiceLabel>): this {
        const isIterable = !(labels instanceof ServiceLabel);
        const toAddIterable: Iterable<ServiceLabel> = isIterable ? labels : [labels];
        const iterator: Iterator<ServiceLabel> = toAddIterable[Symbol.iterator]();
    
        while (this.collection.size < ServiceLabelsCollection.maxSize) {
            const result: IteratorResult<ServiceLabel> = iterator.next(); // Simply I didn't do destructuring
            
            if (result.done) break;
            this.collection.addSoft(result.value);
        }
    
        return this;
    }
}

有人看到我可能在这里犯的错误吗?或者其他人猜测这可能是 TypeScript 错误?

typescript destructuring typescript-eslint
1个回答
0
投票

TLDR:不要手动输入所有内容。让打字稿推断事情是可以的。实际上,您在这里使用了错误的类型,导致类型被扩展为

any
,eslint 现在正在向您大喊大叫。


实际问题

使用

any
值对元组元素进行不安全的数组解构。 eslint@typescript-eslint/无不安全分配

这不是打字稿错误。这是一个 eslint 错误。而问题的根源在于

result.value
的类型是
any
。解构只会导致 eslint 注意到这一点。


找到正确的使用类型

所以真正的问题是

result.value
被输入为
any
。让我们解决这个问题。

我已将您的代码简化为:

type ServiceLabel = { label: string }
const array: ServiceLabel[] = []
const iterator = array[Symbol.iterator]()

const { value, done }: IteratorResult<ServiceLabel> = iterator.next();
//      ^ any

游乐场

现在在您的 IDE(或打字稿游乐场)中,如果您将鼠标悬停在该片段中的迭代器结果上,您将看到此类型:

type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>

请注意,未指定时第二个参数默认为

any
。然后请注意,您只传递了一个参数。

现在将鼠标悬停在

.next()
上,您将看到此类型:

Iterator<ServiceLabel, undefined, unknown>.next(...[value]: [] | [unknown]):
  IteratorResult<ServiceLabel, undefined>

这表示

iterator.next()
返回此类型:

IteratorResult<ServiceLabel, undefined>

与您选择的类型不同。如果您将类型更改为

IteratorResult<ServiceLabel, undefined>
那么一切都应该按您的预期工作。

const { value, done }: IteratorResult<ServiceLabel, undefined> = iterator.next();

或者只是让它被推断,因为在打字稿中你不应该为每个值提供类型。

const { value, done } = iterator.next();

看游乐场


这是怎么回事?

如果您深入了解

IteratorResult
的类型(cmd/ctrl+单击 IDE 中的类型),它会描绘出更大的图景:

interface IteratorYieldResult<TYield> {
    done?: false;
    value: TYield;
}

interface IteratorReturnResult<TReturn> {
    done: true;
    value: TReturn;
}

type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;

迭代器有两种返回值的方式。 “yield”结果是您通常期望迭代器迭代的结果,“return”结果是迭代器“完成”时的值。

ArrayIterator
的情况下,“yield”类型是数组成员的类型,“return”结果是
undefined
,因为当你到达数组末尾时,没有剩余的项目可以返回.

但是如果您手动使用

IteratorResult
类型,则必须提供两种类型以避免
any
,因为
TReturn
类型默认为
any
。这炸毁了这种联合类型:

IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
// ends up this: IteratorYieldResult<T> | IteratorReturnResult<any>;

因为任何与

any
的并集都是
any. e.g. 
,类型 A = number |任何 // 任何`.

© www.soinside.com 2019 - 2024. All rights reserved.