如何以类型安全的形式使用 TypeScript 进行浅拷贝
how to do a shallow copy with TypeScript in a type safe form
const shallowCopy = <T>(obj: T): T => {
const newObj: T = {}; // type error here (ts2322)
const objKeys = Object.keys(obj) as (keyof T)[];
for (const k of objKeys) {
newObj[k] = obj[k];
}
return newObj;
}
第二行有类型问题:
Type '{}' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to '{}'.
我该如何解决?
方法一:Object.assign()
const shallowCopy = <T>(obj: T): T => {
return Object.assign({}, obj);
}
方法二:as
使用 as
强制 TypeScript 解释对象 as
声明的类型。
const shallowCopy = <T>(obj: T): T => {
const newObj: Partial<T> = {};
for (const k of Object.keys(obj)) {
newObj[k] = obj[k];
}
return newObj as T;
}
或
const shallowCopy = <T>(obj: T): T => {
const newObj: T = {} as T;
for (const k of Object.keys(obj)) {
newObj[k] = obj[k];
}
return newObj;
}
警告
以上使用 Object.keys()
因此将仅复制 own
enumerable
属性。 See
此外,如果 T
是不同于 Object
的任何实例,则 prototype
链将被破坏(但 TS 不会检测到它)。我建议使用这个函数,它保留了原型链。
const shallowCopy = <T>(obj: T): T => {
const copy = Object.assign({}, obj);
Object.setPrototypeOf(copy, Object.getPrototypeOf(obj));
return copy;
}
说明
TypeScript 的主要任务之一是确保对象始终与声明的类型一致(JS 中没有强加的东西)。当你有这样的东西时
interface Person {
name: string;
height?: number;
}
let a: Person = {};
// (1)
a.name = 'Time';
// (2)
在 (1)
点,对象 a
将与类型 Person
不一致,因为它缺少 name
属性。在 (2)
点,您使 a
保持一致,因为现在它具有 name
属性 类型 String
。但是,TypeScript 仍然会在 (1)
点抱怨,即使没有代码你有一段时间 a
不遵守定义。
解决方案是告诉 TS a
将是一个 Partial<Person>
这意味着它包含 Person
属性的一部分(或 none)(不是更多和不一样)。然后一旦我们完成赋值(并且我们确定我们分配了所有强制属性)我们(如果需要)强制 a
到 Person
以便下面的代码可以将 a
简单地视为Person
(并假定 已设置 所有必需属性)。
let a: Partial<Person> = {};
a.name = 'Time';
return a as Person;
在这个例子中,如果 T
是一个 POJO,在点 (1)
,newObj
符合类型 T
(已经收到来自类型 T
对象)。 请注意,T
可能是一个 prototype
不同于 Object
的对象,因此 newObj
将不兼容 。即使对于 POJO,我也从未见过任何 TS 实现(也没有 IDE)检测到这种模式,因此我们需要添加 as T
const shallowCopy = <T>(obj: T): T => {
const newObj: Partial<T> = {};
for (const k of Object.keys(obj)) {
newObj[k] = obj[k];
}
// (1)
return newObj as T;
}
您可以将对象创建为 any
类型,然后在复制所有键后将其转换为 T
类型:
const shallowCopy = <T>(obj: T): T => {
const newObj: any = {};
const objKeys = Object.keys(obj) as (keyof T)[];
for (const k of objKeys) {
newObj[k] = obj[k];
}
return newObj as T;
}
const shallowCopy = <T extends Record<string, unknown>>(obj: T) =>
Object.keys(obj).reduce((acc, elem) => ({
...acc,
[elem]: acc[elem]
}), {} as T)
你不需要创建新的空对象并改变他。
如果您在 reduce
中没有任何额外的逻辑,您可能应该坚持使用 @crashmstr
的解决方案
const shallowCopy = <T>(obj: T): T => {
const newObj: T = {}; // type error here (ts2322)
const objKeys = Object.keys(obj) as (keyof T)[];
for (const k of objKeys) {
newObj[k] = obj[k];
}
return newObj;
}
第二行有类型问题:
Type '{}' is not assignable to type 'T'. 'T' could be instantiated with an arbitrary type which could be unrelated to '{}'.
我该如何解决?
方法一:Object.assign()
const shallowCopy = <T>(obj: T): T => {
return Object.assign({}, obj);
}
方法二:as
使用 as
强制 TypeScript 解释对象 as
声明的类型。
const shallowCopy = <T>(obj: T): T => {
const newObj: Partial<T> = {};
for (const k of Object.keys(obj)) {
newObj[k] = obj[k];
}
return newObj as T;
}
或
const shallowCopy = <T>(obj: T): T => {
const newObj: T = {} as T;
for (const k of Object.keys(obj)) {
newObj[k] = obj[k];
}
return newObj;
}
警告
以上使用 Object.keys()
因此将仅复制 own
enumerable
属性。 See
此外,如果 T
是不同于 Object
的任何实例,则 prototype
链将被破坏(但 TS 不会检测到它)。我建议使用这个函数,它保留了原型链。
const shallowCopy = <T>(obj: T): T => {
const copy = Object.assign({}, obj);
Object.setPrototypeOf(copy, Object.getPrototypeOf(obj));
return copy;
}
说明
TypeScript 的主要任务之一是确保对象始终与声明的类型一致(JS 中没有强加的东西)。当你有这样的东西时
interface Person {
name: string;
height?: number;
}
let a: Person = {};
// (1)
a.name = 'Time';
// (2)
在 (1)
点,对象 a
将与类型 Person
不一致,因为它缺少 name
属性。在 (2)
点,您使 a
保持一致,因为现在它具有 name
属性 类型 String
。但是,TypeScript 仍然会在 (1)
点抱怨,即使没有代码你有一段时间 a
不遵守定义。
解决方案是告诉 TS a
将是一个 Partial<Person>
这意味着它包含 Person
属性的一部分(或 none)(不是更多和不一样)。然后一旦我们完成赋值(并且我们确定我们分配了所有强制属性)我们(如果需要)强制 a
到 Person
以便下面的代码可以将 a
简单地视为Person
(并假定 已设置 所有必需属性)。
let a: Partial<Person> = {};
a.name = 'Time';
return a as Person;
在这个例子中,如果 T
是一个 POJO,在点 (1)
,newObj
符合类型 T
(已经收到来自类型 T
对象)。 请注意,T
可能是一个 prototype
不同于 Object
的对象,因此 newObj
将不兼容 。即使对于 POJO,我也从未见过任何 TS 实现(也没有 IDE)检测到这种模式,因此我们需要添加 as T
const shallowCopy = <T>(obj: T): T => {
const newObj: Partial<T> = {};
for (const k of Object.keys(obj)) {
newObj[k] = obj[k];
}
// (1)
return newObj as T;
}
您可以将对象创建为 any
类型,然后在复制所有键后将其转换为 T
类型:
const shallowCopy = <T>(obj: T): T => {
const newObj: any = {};
const objKeys = Object.keys(obj) as (keyof T)[];
for (const k of objKeys) {
newObj[k] = obj[k];
}
return newObj as T;
}
const shallowCopy = <T extends Record<string, unknown>>(obj: T) =>
Object.keys(obj).reduce((acc, elem) => ({
...acc,
[elem]: acc[elem]
}), {} as T)
你不需要创建新的空对象并改变他。
如果您在 reduce
中没有任何额外的逻辑,您可能应该坚持使用 @crashmstr
的解决方案