将打字稿版本升级到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,有什么方法可以实现这种正确的方法吗?
答案 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]
的通用key
,K
也不起作用。您正在从通用类型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
。
无论如何,这些将按需工作。就我个人而言,我可能只使用类型断言并继续。
好的,希望能有所帮助;祝你好运!
答案 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#