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

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

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

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

Q&A

解決済

3回答

1273閲覧

Objectに対する文字列プロパティアクセスをする時、プロパティの型を絞り込みたい

UnripeTomato

総合スコア25

TypeScript

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

0グッド

0クリップ

投稿2021/10/21 07:06

編集2021/10/21 07:14

いつもお世話になります。
TypeScriptの型推論について、ご教示いただければ幸いです。

前提・実現したいこと

TypeScript 4.3を利用しています。

異なるオブジェクト同士(objA, objB)でキー名を比較して、同一キー名の型が同じ時のみ、
片側のオブジェクトにもう片方のプロパティを代入する良い書き方があればご教示いただけませんでしょうか。

片側のオブジェクトのkeyで回した時、どうしてもnever型で推論してしまい、エラーになってしまいます。

発生している問題・エラーメッセージ

Type 'string' is not assignable to type 'never'.

エラーその2
こちらは、baseClass のidに readonlyを付けた場合、当該エラー箇所が id が入ることを示唆していてエラーとなる。
このことで、当該箇所でプロパティの絞り込みができていないと考えています。

Cannot assign to 'id' because it is a read-only property.

該当のソースコード

TypeScript

1class baseClass { 2 constructor(id: number,name: string ,name2: string){ 3 this.id = id 4 this.name = name 5 this.name2 = name2 6 } 7 id:number 8 name:string 9 name2:string 10} 11const baseClassInstance = new baseClass(0,"","") 12baseClassInstance.id = 123 13baseClassInstance.name = "base name" 14baseClassInstance.name2 = "base name 2" 15 16const forInput = { 17 id: 789, 18 name2: "input 2", 19 name10 : "input 10", 20} 21 22Object.keys(forInput) 23 .forEach(key => { 24 if (typeof baseClassInstance[key as keyof typeof baseClassInstance] === "string" && 25 typeof forInput[key as keyof typeof forInput] === "string" 26 ) { 27 console.log(key) 28 console.log(forInput[key as keyof typeof forInput]) 29 console.log(typeof forInput[key as keyof typeof forInput]) 30 console.log(baseClassInstance[key as keyof typeof baseClassInstance]) 31 console.log(typeof baseClassInstance[key as keyof typeof baseClassInstance]) 32 33 // 以下のような挙動を取りたい 34 // baseClassInstance["name2"] = forInput["name2"] 35 baseClassInstance[key as keyof typeof baseClassInstance] = forInput[key as keyof typeof forInput] 36 //ops... Type 'string' is not assignable to type 'never'. 37 } 38 }) 39 40//期待する結果 41//forInput の name2だけが string型で共通するので、そのプロパティが書き換わっている 42console.log(baseClassInstance) 43// baseClass: { 44// "id": 123, 45// "name": "base name", 46// "name2": "input 2" 47// }

試したこと

エラーを無視すれば実行時に動くのですが、もっと正しい示唆の仕方がないのかなと悩んでいます。

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

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

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

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

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

guest

回答3

0

自己解決されたようで何よりです。

ちなみにエラーが発生する行の(コメントアウトされている行は含まずに、)一行上の行の行末にセミコロンを追加

diff

1- console.log(typeof baseClassInstance[key as keyof typeof baseClassInstance]) 2+ console.log(typeof baseClassInstance[key as keyof typeof baseClassInstance]);

したうえで、エラーが発生する行を以下のように修正

diff

1- baseClassInstance[key as keyof typeof baseClassInstance] = forInput[key as keyof typeof forInput] 2+ (baseClassInstance[key as keyof typeof baseClassInstance] as string | number) = forInput[key as keyof typeof forInput]

することによっても、エラーは解消されました。

???? 上記の修正を Typescript Playground で動作確認

投稿2021/10/21 16:17

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

UnripeTomato

2021/10/22 00:25

おおぉぉぉ・・・ご回答いただきありがとうございます。動作確認しました。 `as string | number` とするということは、やはりifでの型ガードは効いていないってことなのですね???? このスレッドでの新たな質問となり恐縮ですが、ご提案頂いている、 > 一行上の行の行末にセミコロンを追加 確かになくすと構文エラーになるのですが。。。 これは、どのような作用を引き起こしているのでしょうか?????
退会済みユーザー

退会済みユーザー

2021/10/22 16:19

セミコロンが無いと、 見た目上、 console.log(typeof baseClassInstance[key as keyof typeof baseClassInstance]) と (baseClassInstance[key as keyof typeof baseClassInstance] as string | number) = forInput[key as keyof typeof forInput] とは別の行ですが、連結された console.log(typeof baseClassInstance[key as keyof typeof baseClassInstance])(baseClassInstance[key as keyof typeof baseClassInstance] as string | number) = forInput[key as keyof typeof forInput] でひとつの文と見做されており、これがコンパイルされてしまうので、文法エラーになってしまうようです。
guest

0

自己解決

もう少し書きようがあるとはおもうのですが・・・
型ガード関数を実装してチェックすることで、構文エラーを通すことができました!

TypeScript

1console.clear(); 2class baseClass { 3 constructor(id: number, name: string, name2: string) { 4 this.id = id; 5 this.name = name; 6 this.name2 = name2; 7 } 8 id: number; 9 name: string; 10 name2: string; 11} 12 13const forInput = { 14 id: 987897, 15 name2: "input name", 16 name10: "input name 10", 17}; 18 19let objInstance = new baseClass(123, "base1", "base2"); 20 21// want to run like as follow. 22// objInstance["name"] = "aaa" 23Object.keys(forInput).forEach((key) => { 24 if ( 25 typeof objInstance[key as keyof typeof objInstance] === "string" && 26 typeof forInput[key as keyof typeof forInput] === "string" 27 ) { 28 29 //ここで型ガード関数作成 30 const isString = (value: unknown): value is string => 31 typeof value === "string"; 32 33 if (isString(objInstance[key as keyof typeof objInstance])) { 34 if (isString(forInput[key as keyof typeof forInput])) { 35 //代入する時も as string にキャストしてから入れる 36 (objInstance[key as keyof typeof objInstance] as string) = forInput[ 37 key as keyof typeof forInput 38 ] as string; 39 } 40 } 41 } 42}); 43console.log(objInstance); 44期待する結果となった 45//baseClass { id: 123, name: 'base1', name2: 'input name' }

tsc後のjs抜粋

JavaScript

1Object.keys(input).forEach((key) => { 2 if (typeof objInstance[key] === "string" && typeof input[key] === "string") { 3 const isString = (value) => typeof value === "string"; 4 if (isString(objInstance[key])) { 5 if (isString(input[key])) { 6 objInstance[key] = input[key]; 7 } 8 } 9 } 10});

投稿2021/10/21 15:25

編集2021/10/21 15:29
UnripeTomato

総合スコア25

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

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

0

TypeScriptの型はコンパイル時点で消えてしまうが故に、実行時に型情報を参照できないという制約がつきまといます。

「事前にbaseClassのうちstringとなるキーの配列をas constとして作成しておく」ような手段を除けば、「エラーを無視させる」しかありません。

投稿2021/10/21 08:21

編集2021/10/21 08:21
maisumakun

総合スコア145208

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

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

UnripeTomato

2021/10/21 15:32 編集

回答ありがとうございます! >「事前にbaseClassのうちstringとなるキーの配列をas constとして作成しておく」 この発想は無かったです!ありがとうございます! 実行時に型情報が飛ぶが故に、静的解析時に安全性を高めようと色々と方言に沿った書き方をしていかないとダメなんですね・・・。 今回はエラーを無視させない方法(tsの方言をasで騙しただけかもしれないですが)ができましたので自己解決とさせていただきましたが、ご提案いただいた手段もあること勉強になりました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問