我正在尝试将属性值从源S
复制到目标T
。在以编程方式检查字段是否存在后,我希望TS允许我访问它,但不会:
● Test suite failed to run
src/util/Copy.ts:18:18 - error TS2536: Type 'keyof T' cannot be used to index type 'S'.
18 return source[field] === target[field];
~~~~~~~~~~~~~
src/util/Copy.ts:33:11 - error TS2536: Type 'keyof S' cannot be used to index type 'T'.
33 match[field] = source[field];
~~~~~~~~~~~~
我可以让源从目标扩展到只接受两种类型的字段。但是我的实用方法甚至可以处理完全不同的类型。下面是失败的代码
export function copyFieldsWhenMatching<S extends object, T extends object>(
sourceArray: S[],
targetArray: T[],
equalFields: (keyof T)[],
fieldsToCopy: (keyof S)[],
allowMultipleOccurrences: boolean
): T[] {
const toReturn = [...targetArray];
for (const source of sourceArray) {
// Find matches based on equalFields
const matches = toReturn.filter((target) => {
return equalFields.every((field) => {
// eslint-disable-next-line no-prototype-builtins
if (source.hasOwnProperty(field) && target.hasOwnProperty(field)) {
return source[field] === target[field];
}
return true; // Property doesn't exist in either source or target; continue matching.
});
});
if (!allowMultipleOccurrences && matches.length > 1) {
throw new DuplicateMatchesError(
"More than one match found for the source object.",
matches
);
} else if (matches.length >= 1 || allowMultipleOccurrences) {
// Copy fields from source to matches
for (const match of matches) {
for (const field of fieldsToCopy) {
match[field] = source[field];
}
}
}
}
return toReturn;
}
这就是我如何破解它通过(铸造目标源和反向只是为了通过)。这就引出了标题中的问题:
有没有更干净的方法来更新T的keyof值?
import { DuplicateMatchesError } from "../errors";
export function copyFieldsWhenMatching<S extends object, T extends object>(
sourceArray: S[],
targetArray: T[],
equalFields: (keyof T)[],
fieldsToCopy: (keyof S)[],
allowMultipleOccurrences: boolean,
): T[] {
const toReturn = [...targetArray];
for (const source of sourceArray) {
// Find matches based on equalFields
const matches = toReturn.filter((target) => {
return equalFields.every((field) => {
// eslint-disable-next-line no-prototype-builtins
if (source.hasOwnProperty(field) && target.hasOwnProperty(field)) {
return (
// dirty assertion to hack TS
(source as unknown as T)[field] ===
target[field]
);
}
return true; // Property doesn't exist in either source or target; continue matching.
});
});
if (!allowMultipleOccurrences && matches.length > 1) {
throw new DuplicateMatchesError(
"More than one match found for the source object.",
matches,
);
} else if (matches.length >= 1 || allowMultipleOccurrences) {
// Copy fields from source to matches
for (const match of matches) {
for (const field of fieldsToCopy) {
// dirty assertion to hack TS
(match as unknown as S)[field] = source[field];
}
}
}
}
return toReturn;
}
2条答案
按热度按时间a8jjtwal1#
我建议将所有数组强制转换为
(S & T)[]
pbossiut2#
在示例中的这段代码中,您使用
hasOwnProperty()
来检查source
是否具有属性。但是,hasOwnProperty()
不会缩小其目标,因此TS不会缩小source
以包含field
指定的属性。要解决这个问题,您可以创建一个 * 用户定义的类型保护 *,它调用
hasOwnProperty()
并缩小目标对象。TypeScript在某些情况下缩小了变量的类型,例如使用
typeof
时。通过使用一个名为 type predicate 的返回类型,我们可以创建 functions 来缩小其输入的类型。这些被称为 * 用户定义的类型保护 *。下面是
hasOwnProperty()
的例子:你可以这样使用它:
注意:这是“意味着”与字符串键一起使用。由于
field
的类型为keyof T
,因此它实际上将source
类型为S & Record<keyof T, unknown>
(即具有T
的所有属性)。但是,由于只是使用相同的变量(field
)来索引source
,所以没有问题。但要小心,如果你做得更多。这是你的代码:TypeScript Playground。
有关类型保护的更多信息,请参阅TS文档和优秀的TypeScript Deep Dive by basarat。