✅
TypeScriptでプロパティ代入が「is not assignable to 'never'」となるエラーの対策
はじめに
まずサンプルコードですが、対象のオブジェクトを、別のオブジェクトでプロパティ上書きするような感じの処理になります。
// 仮の型定義、宣言
interface Note {
id: number,
label: string,
value: number
}
declare var noteList: Note[]
function updateNote(param: Note) {
// note取得(簡易のため、必ず成功するものとする)
const targetNote = noteList.find((note)=> note.id === param.id)!;
// 更新
const propKeys = Object.keys(param) as (keyof Note)[]
propKeys.forEach((key) => {
// idは更新しない
if (key === "id") return
// ※
targetNote[key] = param[key]
}
一見すると問題ない(そして動作上も問題ない)ですが、strictルール下では※のプロパティ(Index Signature)代入のところで、
Type 'string | number' is not assignable to type 'never'.
なるエラーが発生します。
strictルールを解いたり@ts-ignore
での無視も可能なものの、やはり気持ちが悪いので解決していこうと思います。
原因と対策
プロパティ値のparam[key]
の型はそのままだとstring|number
になりますが、この型に厳密に適合するプロパティがNote
に存在しないのでエラーになるようです。
しかしif-else文やswitch文でキーごとに場合分けをするとparam[key]
の型がstringあるいはnumberいずれかに確定するため、エラーを解消できます。
// idは更新しない
if (key === "id") return
// NG: neverエラー
targetNote[key] = param[key]
// OK:if-elseで場合分け
if (key === "value") {
// param[key]がnumber型に確定
targetNote[key] = param[key]
} else {
// 同じくstring型に確定
targetNote[key] = param[key]
}
// OK:switch文で場合分け
switch (key) {
case "value":
targetNote[key] = param[key]
break
case "label":
targetNote[key] = param[key]
break
}
またお勧めはしませんが、以下のようにアサーションで強引に型の不適合を解消することもできます。
(キー数が多くて場合分けが面倒なときは有効?)
// "value"以外でもOK(この例では"label"や"id"など、存在するキー名であれば)
targetNote[key as "value"] = param[key] as any
その他
- あくまで代入すべき値の型が適合しないことがNGのようなので、例えば
label
プロパティ値の型がnumberだった場合、(どのプロパティもnumber型は代入可能なので)エラーは発生しません- この点から「
never
になるからNGだよ!」とエラーを出すのは微妙に不親切というか混乱のもとという気も…
- Noteインターフェースには
"label"|"value"
というunion型に対応するプロパティキーが存在しないのでneverになる、と解釈も可能だけども...?
- この点から「
Discussion