233
122

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

TypeScript3.4 の const assertion

Last updated at Posted at 2019-02-01

const assertion は TypeScript3.4 の新機能です。Const contexts for literal expressions
すでに master に入っているので、nightly build で挙動を確認することができます。

$ yarn add typescript@next

VSCode のワークスペースバージョンを切り替えれば、本稿内容の挙動を確認できます。const assertion により、個人的に待望だった widening Literal Types の抑止が叶います。

widening Literal Types って何?

従来、const 宣言代入で得られた Literal Types は widening 挙動により、可変の変数に代入すると、Literal Types ではなくなってしまう仕様がありました。参考:https://qiita.com/Takepepe/items/2c06f65a51a12ffe4d61

NonWidening にするため次のように、全く同じ文字列で assertion を付与する必要がありました(または annotation)。

const widening = 'LITERAL_TYPES_TEST'
const nonwidening = 'LITERAL_TYPES_TEST' as 'LITERAL_TYPES_TEST'
const check = {
  widening,
  nonwidening
}
// const widening: 'LITERAL_TYPES_TEST'
// const nonwidening: 'LITERAL_TYPES_TEST'
// const check = {
//   widening: string <- !?
//   nonwidening: 'LITERAL_TYPES_TEST'
// }

それが今回の変更で、次のように as const アサーションを付与すれば NonWidening になる様になりました。

const widening = 'LITERAL_TYPES_TEST' as const
const nonwidening = 'LITERAL_TYPES_TEST' as 'LITERAL_TYPES_TEST'
const check = {
  widening,
  nonWidening
}
// const widening: 'LITERAL_TYPES_TEST'
// const nonWidening: 'LITERAL_TYPES_TEST'
// const check = {
//   widening: 'LITERAL_TYPES_TEST' <- 🎉
//   nonWidening: 'LITERAL_TYPES_TEST'
// }

次のような書き方でも widening 挙動を抑制可能です。readonly もついてきます。

const ONE = 'ONE'
const TWO = 'TWO'
const THREE = 'THREE'

const widening = { ONE, TWO, THREE }
// const widening: {
//   ONE: string
//   TWO: string
//   THREE: string
// }
const nonwidening = { ONE, TWO, THREE } as const
// const nonwidening: {
//   readonly ONE: 'ONE'
//   readonly TWO: 'TWO'
//   readonly THREE: 'THREE'
// }
const enumLike = {
  FOUR: 'FOUR',
  FIVE: 'FIVE',
  SIX: 'SIX'
} as const
// const enumLike: {
//   readonly FOUR: 'FOUR'
//   readonly FIVE: 'FIVE'
//   readonly SIX: 'SIX'
// }

export = {} as const もばっちりです。

export = {
  SEVEN: 'SEVEN',
  EIGHT: 'EIGHT',
  NINE: 'NINE'
} as const

Tuple 宣言が楽になるぞ

Tuple 宣言の Assertion も冗長でなくなります。

const a  = [0, 1, 2]
const t1 = [0, 1, 2] as [0, 1, 2]
const t2 = [0, 1, 2] as const

// const a: number[]
// const t1: [0, 1, 2]
// const t2: readonly [0, 1, 2]

オブジェクトリテラルの戻り型推論が固定できるぞ

変更できないはずの、オブジェクトリテラルに含まれる戻り値型も Widening 扱いです。これは悔しい…

function wideningIncrement() {
  return { type: 'INCREMENT' }
}
function wideningDecrement() {
  return { type: 'DECREMENT' }
}
function wideningSetCount(amount: number) {
  return { type: 'SET_COUNT', payload: { amount } }
}
// function wideningIncrement() {
//   { type: string } <- !?
// }
// function wideningDecrement() {
//   { type: string } <- !?
// }
// function wideningSetCount(amount: number) {
//   type: string <- !?
//   payload: { amount: number }
// }

これも as const で一発です。

function nonWideningIncrement() {
  return { type: 'INCREMENT' } as const
}
function nonWideningDecrement() {
  return { type: 'DECREMENT' } as const
}
function nonWideningSetCount(amount: number) {
  return { type: 'SET_COUNT', payload: { amount }} as const
}
// function nonWideningIncrement() {
//   readonly { type: 'INCREMENT' } <- 🎉
// }
// function nonWideningDecrement() {
//   readonly { type: 'DECREMENT' } <- 🎉
// }
// function nonWideningSetCount(amount: number) {
//   readonly type: 'SET_COUNT'; <- 🎉
//   payload: { readonly amount: number };
// }

配列の中身が Literal Union Types でとれるぞ

type LegacyAssertActionTypestype LazyAssertActionTypes の型は一緒です。

const legacyAssertActions = [
  'INCREMENT' as 'INCREMENT',
  'DECREMENT' as 'DECREMENT',
  'SET_COUNT' as 'SET_COUNT'
]
const lazyAssertActions = [
  'INCREMENT',
  'DECREMENT',
  'SET_COUNT'
] as const

type Unpacked<T> = T extends { [K in keyof T]: infer U } ? U : never
type LegacyAssertActionTypes = Unpacked<typeof legacyAssertActions>
type LazyAssertActionTypes = Unpacked<typeof lazyAssertActions>
// type LegacyAssertActionTypes = 'INCREMENT' | 'DECREMENT' | 'SET_COUNT'
// type LazyAssertActionTypes   = 'INCREMENT' | 'DECREMENT' | 'SET_COUNT'

type Unpacked<T> についてはこちらを参照ください。
TypeScript3.1 で ReduxAction型定義は不要になりました

JavaScript らしさを損なわないために導入されているであろう widening 挙動。しっかり型付けしたい・けれども冗長さを払拭していきたい、という意向が伺えますね。最高です。

233
122
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
233
122

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?