TypeScriptを使っていて、コピー先のオブジェクトに存在するプロパティのみを、コピー元からコピーしたいということがありました。
「Object.assignじゃダメなの?」と思われるかもしれないですが、Object.assignだとコピー先にないプロパティまでコピーされてしまいます。
const target = { a: 1, b: 2 }
const source = { b: 4, c: 5 }
Object.assign(target, source)
console.log(target) // { a: 1, b: 4, c: 5 }となるが、{ a: 1, b: 4 }となってほしい
これをTypeScriptで実現する方法について調べたのでメモとして残しておきます。
ちなみに、JavaScriptならすぐに書けます。
// 存在するプロパティの値のみコピーする
function copySameProperties(target, source){
for(const key in source){
if(key in target){
target[key] = source[key]
}
}
return target
}
const target = { a: 1, b: 2 }
const source = { b: 4, c: 5 }
copySameProperties(target, source)
console.log(target) // {a: 1, b: 4}
ここまで書けるなら後は、TypeScriptで型を指定すればいいだけかと思ったのですが、これがなかなか難しい…。最終的に、下記のように書くことでうまくいきました。
function copySameProperties<T extends object, U extends object>(
target: T,
source: U
): T {
for (const key in source) {
if (key in target) {
target[key as unknown as keyof T] = source[key] as unknown as T[keyof T];
}
}
return target;
}
const target = { a: 1, b: 2 }
const source = { b: 4, c: 5 }
copySameProperties(target, source)
console.log(target) // {a: 1, b: 4}
ポイントとして下記の3つです。
1.引数のtargetとsourceがオブジェクトということを明示するため、ジェネリクスに「extends object」という制約をいれる
参考:型引数の制約 | TypeScript入門『サバイバルTypeScript』
2.keyの型が「Extract
参考:型アサーション「as」(type assertion) | TypeScript入門『サバイバルTypeScript』
3.2と同様に、source[key]の型がtarget[key]か判別できないため、「T[keyof T]」と型指定をいれている。
「extends object」とか、「as unknown as」とか、TypeScriptに慣れていないとなかなかでてこない書き方で、迷いました…。これぐらい、すんなり書けるようになりたいです。
ちなみに、ChatGPTでJavaScriptのコードを渡して、TypeScriptに変換してと伝えると、第2引数のsourceは「Partial
function copySameProperties<T extends object>(
target: T,
source: Partial<T>
): T {
for (const key in source) {
if (key in target) {
target[key as keyof T] = source[key]!;
}
}
return target;
}
const target = { a: 1, b: 2 }
const source = { b: 4, c: 5 }
copySameProperties(target, source)
console.log(target) // {a: 1, b: 4}
※ChatGPTの提案では「extends object」が無かったり、「key as any」とされてしまったので少し書き換えてます。

コメント