honesome
@honesome (honesome)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

typescript 関数型とその戻り値について

質問したいこと

typescriptの関数型について分からないことがあったのでお聞きしたいです。

type A = {
    a: string
    b: string
}

type B = () => A

const b: B = () => ({
    a: '', b: '', c: ''
})

上記の関数bのところで実際はAの型を返していないのに
何故コンパイルエラーとならないかをお聞きしたいです。

というのも同じような形でReduxのReducerを作っている所で、
コンパイルエラーにならないことに気づき、質問した次第です。

reducer.ts

type State = {
  a: string
  b: string
)


const reducer: Reducer<State, Action> = (state=initialState, action) => {
  switch(action.type) {
     case 'foo':
       return {...state, c: ''}
     default:
       return state   
  }
}

戻り値の型を記載すれば、エラーになることはわかっているのですが、
書かなくてもエラーになってくれないかなと疑問に思っております。

const reducer: Reducer<State, Action> = (state=initialState, action): State => {
  switch(action.type) {
     case 'foo':
       return {...state, c: ''}
     default:
       return state   
  }
}

よろしくお願いいたします

0

3Answer

お気づきの通り、部分型と関係しています。変数 b に代入される関数 () => ({ a: '', b: '', c: '' }) の型は () => { a: sring; b: string; c: string } と推論されます。返り値部分の型は A と互換性があるため、全体として型 () => A と互換性があることになります。

戻り値の型を記載すれば、エラーになることはわかっているのですが、

return にオブジェクトリテラルを書いたときは余計なプロパティがあるとエラーを出してくれますが、変数を書くと通る仕様になっています。

type A = {
    a: string
    b: string
}

type B = () => A

const b1: B = (): A => {
    return { a: '', b: '', c: '' } // Error: Type '{ a: string; b: string; c: string; }' is not assignable to type 'A'.
}

const b2: B = (): A => {
    const a = { a: '', b: '', c: '' }
    return a // OK
}

変数の場合でもエラーを出してくれるような書き方はないので厳密さは諦めた方がいいと思います。

オブジェクトに余計なプロパティがあるとどうしても困る(たとえば受け取ったオブジェクトを REST API に POST する関数があったとして、未知のプロパティを送るとエラーになってしまうなど)というときはオブジェクトの型を検証する型を作るとよさそうです。 https://fettblog.eu/typescript-match-the-exact-object-shape/

1Like

Your answer might help someone💌