打字稿3.7部分,不能分配为永不/未定义类型

时间:2019-11-07 12:51:39

标签: typescript

将打字稿版本升级到3.7后,我在2个函数中遇到打字错误。这些功能可以正常工作(添加@ ts-ignore时一切正常)。

interface A {
    x: string,
    y: number,
}

interface B {
    x: string,
    y: number,
    z: boolean,
}

function extract(input: B, keys: Array<keyof A>): Partial<A> {
    const extract: Partial<A> = {};
    keys.forEach((key: keyof A) => {
        extract[key] = input[key]; // error!
    //  ~~~~~~~~~~~~ <-- 'string | number' is not assignable to 'undefined'    
    });
    return extract;
}

function assign(target: B, source: Partial<A>): void {
    (Object.keys(source) as Array<keyof A>).forEach((key) => {
        target[key] = source[key]!; // error!
    //  ~~~~~~~~~~~ <-- 'string | number' is not assignable to type 'never'
    });
}

const test: B = { x: "x", y: 1, z: true };
console.log(extract(test, ["y"])); // -> { y: 1 }
assign(test, { x: "new" });
console.log(test); // -> { x: "new", y: 1, z: true }

有关代码和错误的信息,请访问ts playground

没有@ ts-ignore,有什么方法可以实现这种正确的方法吗?

2 个答案:

答案 0 :(得分:2)

这是TypeScript 3.5中引入的known breaking change,可防止对索引访问类型的不正确写入。如您所见,它通过捕获实际的错误而产生了良好的效果,并且通过错误地警告完全安全的任务而产生了一些不幸的结果。

解决此问题的最简单方法是使用a type assertion

(extract as any)[key] = input[key];
(target as any)[key] = source[key];

有比any更安全的断言,但表达起来更加复杂。


如果要避免类型断言,则需要使用一些解决方法。对于extract(),在forEach()内使用generic回调函数就足够了。编译器认为分配既是 identical 泛型类型Partial<A>[K]的值,又是它的值,它允许:

function extract(input: B, keys: Array<keyof A>): Partial<A> {
    const extract: Partial<A> = {};
    keys.forEach(<K extends keyof A>(key: K) => {
        extract[key] = input[key];
    });
    return extract;
}

对于assign(),即使使用类型target[key] = source[key]的通用keyK也不起作用。您正在从通用类型NonNullable<Partial<A>[K]>读取并写入不同通用类型B[K]。 (我的意思是“不同”,在某种意义上说,编译器并不代表它们相同;当评估它们时,它们当然是同一类型。)我们可以通过将target变量扩展为{ {1}}(这很好,因为如果您斜视而不考虑突变,则每个Partial<A>也是B)。

所以我会这样:

Partial<A>

哦,我还添加了function assign(target: B, source: Partial<A>): void { const keys = Object.keys(source) as Array<keyof A>; const widerTarget: Partial<A> = target; keys.forEach(<K extends keyof A>(key: K) => { if (typeof source[key] !== "undefined") { // need this check widerTarget[key] = source[key]; } }); } 检查,因为允许undefined;语言不是真的distinguish missing from undefined


无论如何,这些将按需工作。就我个人而言,我可能只使用类型断言并继续。

好的,希望能有所帮助;祝你好运!

Link to code

答案 1 :(得分:0)

我使用助手功能找到了以下解决方案:

interface A {
    x: string,
    y: number,
}

interface B {
    x: string,
    y: number,
    z: boolean,
}

// Using this method, is better accepted by the transpiler
function typedAssign<T>(source: T, target: Partial<T>, key: keyof T, force: boolean = false) {
    if (force) {
        target[key] = source[key]!;
    } else {
        target[key] = source[key];
    }
}

function extract(input: B, keys: Array<keyof A>): Partial<A> {
    const extract: Partial<Pick<B, keyof A>> = {};
    keys.forEach((key: keyof A) => {
        typedAssign(input, extract, key);
    });
    return extract;
}

function assign(target: B, source: Partial<A>): void {
    (Object.keys(source) as Array<keyof A>).forEach((key) => {
        typedAssign(source, target, key, true);
    });
}

const test: B = { x: "x", y: 1, z: true };
console.log(extract(test, ["y"])); // -> { y: 1 }
assign(test, { x: "new" });
console.log(test); // -> { x: "new", y: 1, z: true }

TS游乐场: http://www.typescriptlang.org/play/?ssl=1&ssc=1&pln=37&pc=53#