TL;DR

  • TypeScript は、値だけでなく文脈も考慮して型を推論する。
  • そのため、値とそれが使われる文脈を切り離すと、型エラーが起きることがある。
  • 解消するには、リテラルを直接渡す、型アノテーションを付ける、const を使う、as const や satisfies を付ける、のいずれかを使う。

内容

文脈と型推論

TypeScript は、値が使われる文脈も考慮して型を推論する。そのため、関数やメソッドの抽出といったリファクタリングで値と文脈が切り離されると、型エラーが起きることがある。以下、4 つの例で解消方法を見る。

文字列リテラル

type Language = 'JavaScript' | 'TypeScript' | 'Python'
function setLanguage(language: Language) { ... }
 
let language = 'JavaScript'
//  ^? let language: string
setLanguage(language)
// Argument of type 'string' is not assignable to parameter of type 'Language'.

リテラルを直接渡す(setLanguage('JavaScript'))か、型アノテーションを付ける(let language: Language = 'JavaScript')か、let の代わりに const を使えば解消する。

タプル型

function panTo(where: [number, number]) { ... }
 
const loc = [10, 20]
//    ^? const loc: number[]
panTo(loc)
// Argument of type 'number[]' is not assignable to parameter of type '[number, number]'.

リテラルを直接渡す(panTo([10, 20]))か、型アノテーションを付ける(const loc: [number, number] = [10, 20])と解消する。

as const を付ける方法には注意が要る。loc の型は readonly [10, 20] になるので、panTo が自分の管理下にあるなら、引数に readonly を付ける必要がある(項目14)。

function panTo(where: readonly [number, number]) { ... }

また、変数の値が誤っていたとき、型エラーは定義ではなく関数の呼び出し箇所に出る。

const loc = [10, 20, 30] as const
panTo(loc)
// Argument of type 'readonly [10, 20, 30]' is not assignable to
// parameter of type 'readonly [number, number]'.

オブジェクト

function complain(language: GovernedLanguage) { ... }
 
const ts = { language: 'TypeScript', organization: 'Microsoft' }
 
complain(ts)
// Argument of type '{ language: string; organization: string; }'
// is not assignable to parameter of type 'GovernedLanguage'.
// Types of property 'language' are incompatible.
//   Type 'string' is not assignable to type 'Language'.

リテラルを直接渡すか、型アノテーションを付けると解消する。as const や satisfies を付けてもよい(項目20)。

const ts: GovernedLanguage = { language: "TypeScript", organization: "Microsoft" }
const ts = { language: "TypeScript", organization: "Microsoft" } as const
 
const ts = {
  language: "TypeScript",
  organization: "Microsoft",
} satisfies GovernedLanguage

コールバック

function callWithRandomNumber(fn: (n1: number, n2: number) => void) { ... }
 
const fn = (a, b) => console.log(a + b)
//          -     Parameter 'a' implicitly has an 'any' type.
//             -  Parameter 'b' implicitly has an 'any' type.
callWithRandomNumber(fn)

リテラルを直接渡す(callWithRandomNumber((a, b) => console.log(a + b)))か、型アノテーションを付ける。型アノテーションは引数に付けてもよいし、関数式全体に付けてもよい(項目12)。

const fn = (a: number, b: number) => console.log(a + b)
type CallbackFn = (a: number, b: number) => void
function callWithRandomNumber(fn: CallbackFn): void { ... }
 
const fn: CallbackFn = (a, b) => console.log(a + b)

キーワード