質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

Q&A

解決済

3回答

1352閲覧

TypeScript:2つのオブジェクトの値における型同一性保証方法

giwa

総合スコア11

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

0グッド

1クリップ

投稿2019/07/05 20:22

前提・実現したいこと

TypeScriptの型の使い方について質問です。

2つのオブジェクトがあり、それぞれキー名は異なるが同じ型のフィールドを持っています。
片方のフィールドの値をもう片方に代入する関数を書こうと思ったのですが、型の同一性が指定できず、anyで握りつぶす以外のやり方が思いつきませんでした。

anyを使わずに解決できる方法はありませんでしょうか。

該当のソースコード

TypeScript

1const o1 = { 2 age: 23, 3 name: "hoge" 4} 5 6const o2 = { 7 AGE: 31, 8 NAME: "foo" 9} 10 11function set1to2(key1: keyof typeof o1, key2: keyof typeof o2): void { 12 o1[key1] = o2[key2] as any; 13} 14 15set2to1("age", "AGE"); 16set2to1("name", "NAME");

TypeScript Playground

補足情報(FW/ツールのバージョンなど)

TypeScript3.5.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

先程の自己回答をもう少しだけスマートにする方法を見つけました。
異なる型のキーを使用しようとするとコンパイルエラーになります。

ただ、関数の中では型推論が賢くないため?結局anyで潰すことになります。
でも、使う側はコンパイルエラーで守られるので、一応タイプセーフな感じにはなります。

しかし、もう少しスマートなやり方があるんじゃないのかなぁという気がして仕方ないです・・・

typescript

1const o1 = { 2 age: 23, 3 name: "hoge" 4} 5 6const o2 = { 7 AGE: 31, 8 NAME: "foo" 9} 10 11function set2to1<K1 extends keyof typeof o1, K2 extends keyof typeof o2>( 12 key1: typeof o1[K1] extends (typeof o2)[K2] ? K1 : never, 13 key2: typeof o2[K2] extends (typeof o1)[K1] ? K2 : never 14): void { 15 o1[key1] = o2[key2] as any; 16} 17 18set2to1("age", "AGE"); 19set2to1("name", "NAME"); 20set2to1("name", "AGE"); // コンパイルエラー 21set2to1("age", "NAME"); // コンパイルエラー

TypeScriptPlayground

投稿2019/07/06 14:43

giwa

総合スコア11

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

自己解決

一つ方法を思いついたので自己投稿します。
しかし、この方法だとジェネリクスを指定しないとコンパイルが通ってしまうし、ジェネリクスをつけるとすごい冗長になってしまいます。もう少しスマートな方法がありそうなものですが・・・

TypeScript

1const o1 = { 2 age: 23, 3 name: "hoge" 4} 5 6const o2 = { 7 AGE: 31, 8 NAME: "foo" 9} 10 11function set2to1<V, K1 extends keyof typeof o1, K2 extends keyof typeof o2>( 12 key1: typeof o1[K1] extends V ? K1 : never, 13 key2: typeof o2[K2] extends V ? K2 : never 14): void { 15 (o1[key1] as unknown as V) = o2[key2] as unknown as V; 16} 17 18set2to1<number, "age", "AGE">("age", "AGE"); 19set2to1<string, "name", "NAME">("name", "NAME"); 20set2to1<string, "name", "AGE">("name", "AGE"); // ジェネリクスで指定した型以外は指定できない 21set2to1("name", "NAME"); // ジェネリクスを指定しないとコンパイルが通ってしまう

TypeScriptPlayground

投稿2019/07/06 14:18

giwa

総合スコア11

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

typescript

1const o1 = { 2 age: 23, 3 name: "hoge" 4} 5 6const o2 = { 7 AGE: 31, 8 NAME: "foo" 9} 10 11function set2to1<T>(key1: keyof typeof o1, key2: keyof typeof o2): void { 12 ((o1[key1] as unknown) as T) = (((o2[key2] as unknown) as T)); 13} 14 15set2to1<number>("age", "AGE"); 16set2to1<string>("name", "NAME"); 17

のようにTを指定すれば可能だと思います。
ただし、そもそも今回のようにオブジェクトを書き換えるのは、思わぬバグの温床になりそうな気がするので個人的にはあまりやらないと思いますが…

投稿2019/07/06 13:21

s14pes

総合スコア55

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

giwa

2019/07/06 13:56 編集

回答ありがとうございます。しかし、その手法はanyで握りつぶすのとほぼ変わらないかなと思います。 const o1 = { age: 23, name: "hoge" } const o2 = { AGE: 31, NAME: "foo" } function set2to1<T>(key1: keyof typeof o1, key2: keyof typeof o2): void { ((o1[key1] as unknown) as T) = (((o2[key2] as unknown) as T)); } set2to1<number>("age", "NAME"); // "NAME"はstring set2to1<string>("age", "NAME"); // "age"はint 上記のように作成してもコンパイルが通るので、ジェネリクスをつけた意味がありません。 なお、オブジェクトを書き換えるのは今回は例題のために簡易例を出しただけなので、実際のコードはもうすこし現実的なシチュエーションです。(APIで受け取ったあるインタフェースのオブジェクトの値を、別のインタフェースオブジェクトに代入する汎用的なセッタをつくりたい・・・という感じのシチュエーションです。)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問