LoginSignup
0
0

More than 3 years have passed since last update.

Typescript 型についてもう少し詳しく TypeScript & Angular 勉強会 #2

Last updated at Posted at 2020-07-29
1 / 18

はじめに

  • 前回 はちょっと詰め込みすぎたので、その振り返りともういくつかの重要な型についての話をします
  • サンプルコードは基本的にはTypescript Playgroundで実行しています。(オプションはデフォルト)

前回のおさらい


補足1: オブジェクト型の省略可能メンバ

  • オブジェクトを定義するとき、必ずしも必要ではない(あったりなかったりする)メンバがある場合、?でそれを表現できます。
  • 代入されなかったオブジェクトはundefinedとなります(nullではないことに注意)

type Person = {
  firstName: string,
  middleName?: string,
  familyName: string
}

let japanese:Person = {firstName: "Taro", familyName: "Yamada"}
console.log(japanese.middleName) //undefined


補足2: 列挙型(enum)

  • JavaやC#でおなじみの列挙型もあります
    • Javaほどの表現力はなく、C#っぽい感じ
  • 特に何もしなければ、暗黙的にnumber型となる
enum Suit {
  Heart,
  Club,
  Diamond,
  Spade
}

let cardOfHeart: Suit = Suit.Heart
  • string型の指定も可能
enum SuitString {
  Heart = "Heart",
  Club = "Club",
  Diamond = "Diamond",
  Spade = "Spade"
}
  • まぜこぜもOK
enum Color {
  Red = '#c10000',
  Blue = '#007ac1',
  Pink = 0xc10050, //16進数
  White = 255
}

ところで "typescript enum" で検索してみましょう


image.png


あとプログラミングTypescriptの注釈も見ましょう


列挙型の安全な使用には落とし穴が伴うため、列挙型の使用は控えることをお勧めします。 TypeScript には、ほかにもよい表現方法がたくさんあります。 それでも、共同作業者が列挙型の使用を主張し、彼らの考えを変える手立てがない場合は、 彼らが外出している間にいくつかの TSLint ルールを忍者のように忍び込ませ、数値によるア クセスと const でない enum について警告が出るようにしてください。


image.png


列挙体は危険な型

  • 数値型の列挙体は数字ならなんでも代入できる
  • const ではない列挙体はインデックスアクセスで存在しない要素にアクセスできる
enum Suit {
  Heart,
  Club,
  Diamond,
  Spade
}

let cardOfHeart: Suit = Suit.Heart
cardOfHeart = 1 //!!!
let suitInt:number = cardOfHeart

let club = Suit[1] //OK
let invalid = Suit[5] //!!!
console.log(invalid) //undefined

安全に列挙体を使うために

  • 1. 文字列型を必ず利用する
  • 2. const enum にして範囲外アクセスを避ける

const enum SuitString {
  Heart = "Heart",
  Club = "Club",
  Diamond = "Diamond",
  Spade = "Spade"
}

let cardOfDiamond: SuitString = SuitString.Diamond
cardOfDiamond = "Joker" //Type '"Joker"' is not assignable to type 'SuitString'.(2322)
cardOfDiamond = SuitString[2] //A const enum member can only be accessed using a string literal.(2476)

そもそもTypescriptに列挙体は必要か?

  • Union型で同様に安全な定義ができる

type SuitUnion = "Heart" | "Club" | "Diamond" | "Spade"
let spade: SuitUnion = "Spade"

image.png
※ 補完も効きます

  • Union型で実現できないことがしたければ使ってもいいが、おそらく限定的
    • 要素の列挙3
    • ビットフラグ的使い方

前回の課題おさらい①

  • 以下のコードはエラーになります。なぜエラーになるかを説明してください。また、エラーにならないように修正してください。

function SomeOrNull(flag: boolean, value: string) {
  return flag ? value : null
}

let value = SomeOrNull(true, "Hoge")
console.log(value.length) // error!!

参考回答

関数 SomeOrNull はUnion型 string | null を返す関数である。
したがって変数valueの型は string | null 型であるため、このままでは string 型のプロパティである length を利用できないため、エラーとなる。
修正には、typeof演算子により、特定スコープ内でのみvalueの型をstringと推論できるようにする必要がある。


function SomeOrNull(flag: boolean, value: string) {
  return flag ? value : null
}

let value = SomeOrNull(true, "Hoge")

if(typeof value == 'string'){
  console.log(value.length)
}

別解(推奨はしませんが、こういう逃げ道もあるということです)


function SomeOrNull(flag: boolean, value: string) {
  return flag ? value : null
}

let value:any = SomeOrNull(true, "Hoge")
console.log(value.length)


前回の課題おさらい②

  • 交差型や合併型がうまく使えそうな事例を一つ考え、実際に型定義をしてみてください

参考回答

合併型(Union)

関数のレスポンスを、例外ではなく戻り値として表現する。


type Error = {
  errorCode: number,
  message: string,
  causedBy?: Error
}
type MaybeString = "string" | Error

function SomeOrError(flag: boolean, value: string) : MaybeString {
  return flag ? value : {errorCode: 1, "flag is not true"}
}
  • 他の言語でOptional, Maybe, Either と呼ばれるもの(の基礎の基礎的な定義)です。
交差型(Intersection)
  • 軽量なクラス拡張として利用できる。

type Person = {
  firstName: string,
  middleName?: string,
  familyName: string
}

type Address = {
  prefecture: string,
  city: string
}

type Questionnaire = {
  question: string,
  answer: string,
  respondent: Person & Address
}

let qa: Questionnaire = {
  question: "好きな言語は?",
  answer: "typescript",
  respondent: {
    firstName: "山田",
    familyName: "太郎",
    prefecture: "Osaka",
    city: "Osaka"
  }
}

演習問題

以下のコードをenumではなくUnionを使う形に書き換えてください。

enum Tiles {
  None = 0,
  AllSimples = 1,
  AllRuns = 1 << 1,
  Reach = 1 << 2,
  SelfDraw = 1 << 3,
  FirstTurnWin = 1 << 4
}

function printTile(tiles: Tiles):string {
  let result = ""

  if(tiles & Tiles.Reach){
    result += "リーチ"
  }

  if(tiles & Tiles.FirstTurnWin){
    result += "一発"
  }

  if(tiles & Tiles.SelfDraw){
    result += "ツモ"
  }

  if(tiles & Tiles.AllSimples){
    result += "断么九"
  }

  if(tiles & Tiles.AllRuns){
    result += "平和"
  }


  return result
}

let tile = Tiles.Reach | Tiles.SelfDraw

console.log(printTile(tile)) //リーチツモ

ヒント

以下のような定義をすることで、Union型の要素一覧を持つことができる。(使わなくてもいいです)


const permissions = ['read', 'write', 'execute'] as const;
type Permission = typeof permissions[number]; // 'read' | 'write' | 'execute'

参考: https://www.kabuku.co.jp/developers/good-bye-typescript-enum



  1. 個人的には、型を最初に設計して明示しておきたいので、一時変数以外は型アノテーションを書きます 

  2. もちろんHaskellやScalaなどの言語には先行して存在するので「特有」は言いすぎですが、JavaやC#などと比べると、という意味では特有と言っていいのかなと 

  3. 定義を工夫することで、ほぼ同様のことは実現できる→ https://www.kabuku.co.jp/developers/good-bye-typescript-enum 

0
0
0

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
0
0