下面的代碼塊產生打字稿錯誤,因為雖然我們知道,foo[k]并且bar[k]是同一型別,TS無法知道(好吧,也許有些神奇它可以,但顯然事實并非如此)
interface IMixed {
a: number;
b: string;
c: boolean;
}
const foo: IMixed = { a: 1, b: 'one', c: true };
const bar: IMixed = { a: 2, b: 'two', c: false };
(Object.keys(foo) as Array<keyof IMixed>).forEach(k => {
foo[k] = bar[k];
// ^^^^^^ Type 'string | number | boolean' is not assignable to type 'never'.
});
當 TS 無法弄清楚我知道是真的某事時,我就拋出它。但在這種情況下,雖然我知道這是一個有效的分配,但我不知道它是哪種型別......我只知道它們是相同的。我無法找到一種優雅的方式在這里使用泛型。
假設我致力于迭代這些屬性的實踐(例如,因為我們希望屬性確實被添加到行中,并且代碼庫中到處都存在類似的代碼......)
簡而言之:我如何斷言這個分配是有效的?
(附帶問題:為什么錯誤將受讓人稱為“從不”型別?)
更新
@captain-yossarian 對可變性提出了一個很好的觀點,并提出了一個完全可行的不可變解決方案。不過,我覺得這個問題仍然懸而未決,如果這個例子有點詭異:
interface IMixed {
a: number[];
b: string[];
c: boolean[];
}
const foo: IMixed = { a: [1], b: ['one'], c: [true] };
const bar: IMixed = { a: [2], b: ['two'], c: [false] };
function intersection<T>(...arrays: T[][]): T[] {
return [...new Set([].concat(...arrays))]
}
(Object.keys(foo) as Array<keyof IMixed>)
.reduce((acc, elem) => ({
...acc,
[elem]: intersection(foo[elem], bar[elem])
// ^^^^^^^^^ Argument of type 'string[] | number[] | boolean[]' is not
// assignable to parameter of type 'string[]'.
}), foo);
關鍵是,賦值顯然要求型別兼容......但這個intersection功能也是如此。所以這就像原始問題的不可變版本。
uj5u.com熱心網友回復:
在這種情況下值得使用reduce而不是forEach:
interface IMixed {
a: number;
b: string;
c: boolean;
}
const foo: IMixed = { a: 1, b: 'one', c: true };
const bar: IMixed = { a: 2, b: 'two', c: false };
(Object.keys(foo) as Array<keyof IMixed>)
.reduce((acc, elem) => ({
...acc,
[elem]: bar[elem]
}), foo);
操場
突變在 TypeScript 中效果不佳。
請參閱相關的問題: 第一,第二,第三,第四和我的文章
更新
(Object.keys(foo) as Array<keyof IMixed>).forEach(k => {
foo[k] = bar[k]; // error
});
你在這里有一個錯誤,因為forEach它reduce是動態的。
bar[k]是 的并集number | string | boolean,以及foo[k]。這意味著在回圈內部可以分配foo['a'] = foo['c']. 這兩個值都是有效的,因為我們期望有一個 ,number | string | boolean但它也是不安全的。這就是為什么 TS 禁止這種行為。
另一方面,reduce有效,因為我們創建了擴展IMixed而不是變異的新物件
更新
為了能夠變異,foo您需要添加indexing到IMixed界面:
type IMixed= {
a: number;
b: string;
c: boolean;
[prop: string]: number | boolean | string
}
const foo: IMixed = { a: 1, b: 'one', c: true };
const bar: IMixed = { a: 2, b: 'two', c: false };
(Object.keys(foo) as Array<keyof IMixed>).forEach(k => {
foo[k] = bar[k];
});
操場
uj5u.com熱心網友回復:
Captain-yossarian 有用地(一如既往)指出,您不能依靠Object.keys(foo).forEach來選擇有效的鍵,因為它提供了 的所有鍵foo,但foo可能是IMixed具有屬性bar不具有的超型別。另外,僅僅因為foo和bar兩者都有一個a屬性(例如)
似乎可以將回圈基于目標 ( foo)的鍵并檢查該鍵是否存在于源 ( bar) 中,同時要求它們通過通用引數共享一個公共子型別,如下所示:
function copyAllProps<ObjectType>(target: ObjectType, source: ObjectType) {
// The `as` below is a bit of a lie :-D
(Object.keys(target) as Array<keyof ObjectType>).forEach(k => {
// NOTE: The type of `k` here is slightly wrong. We told TypeScript
// it's `keyof ObjectType`, but it could be something else, because
// `target` can have keys that `source` doesn't have (`target`'s type can
// be a supertype of `source`'s type). This `if` mitigates that by
// checking that `source` does have the property.
if (k in source) {
target[k] = source[k];
}
});
}
(注意其中的警告。)這只會復制公共屬性。
用法示例:
// Your original examples
let foo: IMixed = { a: 1, b: 'one', c: true };
let bar: IMixed = { a: 2, b: 'two', c: false };
let bar2 = { a: 2, b: false, c: 'two'};
let foo3 = { a: 1, b: 'one', c: true, d: new Date() };
let bar3 = { a: 2, b: 'two', c: false, d: null };
function test() {
copyAllProps(foo, bar); // <== Works
copyAllProps(foo, bar2); // <== Error as desired, same keys but different types for `b` and `c`
copyAllProps(foo3, bar3); // <== Error as desired, same keys but different types for `d`
copyAllProps(foo, {}); // <== Error as desired, source isn't a match
}
游樂場鏈接
uj5u.com熱心網友回復:
這是更新問題(不可變版本)的解決方案......從@captain-yossarian 的回答和@AlekseyL 中汲取靈感。的評論。
function intersection<K extends keyof IMixed>(k: K, mixed1: IMixed, mixed2: IMixed) {
return [...new Set([].concat(mixed1[k], mixed2[k]))]
}
(Object.keys(foo) as Array<keyof IMixed>)
.reduce((acc, elem) => {
return {
...acc,
[elem]: intersection(elem, foo, bar)
}
}, foo);
或者......如何概括一些更漂亮的東西:
function pairwiseProcess<TIn, TOut>(
item1: TIn,
item2: TIn,
iteratee: <K extends keyof TIn>(k: K, item1: TIn, item2: TIn) => TOut
) {
return (Object.keys(item1) as Array<keyof TIn>).reduce((acc, key) => {
return {
...acc,
[key]: iteratee(key, item1, item2)
};
}, {});
}
console.log(pairwiseProcess(foo, bar, intersection));
抱歉,嘗試將其放在https://www.typescriptlang.org/play 中,但它顯示了我在 IDE 上看不到的錯誤(我什至可以使用ts-node)...?
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/326541.html
標籤:打字稿
上一篇:重繪頁面總是重定向到基本URL
