Wiki_TS_Basic - inoueshinichi/Wiki_Web GitHub Wiki

Typescriptの基本

参考

プロパティ

  • ?オプショナルを付加することで, プロパティなしでも動く
function printName(obj: { firstName: string, lastName?: string }) {
  ....
}

printName({ firstName: 'tiny' })

インデックスプロパティ

  • プロパティ名やプロパティの数が事前に決まらない場合の型の決め方
  • { []: 型名 }
e.g. keyに文字列, オブジェクトに文字列または数値
type Label = {
  [key: string]: string | number
}

const labels: Label = {
  topTitle: "トップタイトル",
  subTitle: "サブタイトル",
  statusCode: 200,
  status: true, // booleanを定義指定なのでここでエラーが出る
}

関数型

  • (引数名: 型) => 戻り値
function genBirdsInfo(name: string): string[] {
  return name.split(',')
}

function singBirds(birdsInfo: (x: string) => string[]) : string {
  return birdsInfo('hato,kiji')[0] + 'piyo piyo' // 'hato piyo piyo
}

型アサーション

  • TypeScriptが具体的な型を知ることができないケースで使用する.
  • e.g. document.getElementById('~')の戻り値の型
  • TSは, HTMLElement or null までしかわからない.
  • 具体的にHTMLCanvasElementやHTMLDivElementがわからない.
  • 変数 = 値 as 型: (開発者は型を知っている)
const myCanvasElem = document.getElementById('main_canvas') as HTMLCanvasElement
  • 型アサーションが求められるケース
  1. より具体的な型へのキャスト
  2. より汎化される型へのキャスト
  • 型1 -> any -> 型2
  • const user = (response as any) as User

Interface

  • クラスと親和性が高い拡張ができる
  • implementsを用いてクラスに実装を強制できる
  • interface定義では, プロパティに?オプショナルにすると強制力がなくなる
interface 型名 {
  プロパティ1: 型1
  プロパティ2?: 型2
}

e.g.
interface Point {
  x: number
  y: number
}

function printPoint(point: Point) {
  console.log(point)
}

// 拡張
interface Point {
  z: number // 追加定義
}

printPoint({ x:100, y:50}) // エラー
printPoint({ x: 1, y: 1, z: 1}) // OK

// 実装の強制
class MyPoint implements Point {
  x: number
  y: number
  z: number // zを消すとエラー
}

// interfaceの?オプショナルによるプロパティの強制力の無効化
interface Matrix {
  m11: number
  m12: number
  m13: number
  m14?: number
  m21: number
  m22: number
  m23: number
  m24?: number
  m31: number
  m32: number
  m33: number
  m34?: number
}

class Matrix3D implements Matrix {
  m11: number
  m12: number
  m13: number
  m21: number
  m22: number
  m23: number
  m31: number
  m32: number
  m33: number
} // OK

interfaceの組み合わせによる拡張

  • 複数のinterfaceを組み合わせて拡張できる
interface Colorful {
  color: string
}

interface Circle {
  radius: number
}

interface ColorfulCircle extends Colorful, Circle {}

const cc: ColorfulCircle = {
  color: "青",
  radius: 1.5
}

列挙型

// numberベース
enum Direction {
  Up = 0,
  Down = 1,
  Left = 2,
  Right = 3
}
 
// stringベース
enum Direction {
  Up = 'Up',
  Down = 'Down',
  Left = 'Left',
  Right = 'Right'
}  

ジェネリック型

class Deque<T> {
  private array: T[] = []

  popleft(): T {
    return this.array.shift()
  }

  pop(): T {
    return this.array.pop()
  }

  pushleft(item: T): void {
    this.array.unshift(item)
  }

  push(item: T): void {
    this.array.push(item)
  }

  size(): number {
    return this.array.length
  }
}

const deq: Deque = new Deque<number>()

Union型(|)とInterface型(&)

  • 型エイリアス同士の組み合わせ
type Identity = {
  id: number | string
  name: string
}

type Contact = {
  name: string
  email: string
  phone: string
}

type IdentityORContact = Identity | Contact
// id: number | string
// name: string
// email: string
// phone: string

type Employee = Identity & Contact
// name: string

リテラル型

  • 型を文字や数値で指定したもの
  • 変数: 許可するデータ1 | 許可するデータ2 | ...
let postStatus: 'draft' | 'published' | 'deleted' 
postStatus = 'draft' // OK
postStatus = 'drafts' // NG

function compare(a: string, b: string): -1 | 0 | 1 {
  return a === b ? 0 : a > b ? 1 : -1
}

never型

  • 決して発生しない型
  • 用途: 常に例外を発生させる抽象基底クラスのメソッドの戻り値などに使用する
function error(message: string): never {
  throw new Error(message)
}

function foo(x: string | number | number[]): boolean {
  if (typeof x === 'string') {
    return true
  } else if (typeof x === 'number') {
    return false
  }

  // neverを使用しないとTSコンパイルでエラーが発生する
  return error('Never happen')
}

// Enum + Switch + Never
// 将来的にも定数が追加される可能性があるenum型を定義する
enum PageType {
  ViewProfile,
  EditProfile,
  ChangePassword
}

const getTitleText = (type: PageType) => {
  switch (type) {
    case PageType.ViewProfile:
      return 'View'
    case PageType.EditProfile:
      return 'Edit'
    case PageType.ChangePassword:
      return 'Change'
    default:
      // 決して起きないことをTSコンパイラに通達する
      const wrongType: never = type
      throw new Error(`${wrongType} is not in PageType`)
  }
}

keyofオペレータ

  • その型が持つ各プロパティの型のUnion型を取得できる
  • それは, リテラル型のUnion型
interface User {
  name: string
  age: number
  email: string
}

type UserKey = keyof User;
// UserKey = name | age | email

const key1: UserKey = 'name' // OK
const key2: UserKey = 'phone' // NG

第一引数に渡したオブジェクトの型のプロパティ名のUnion型と 第二引数に渡したオブジェクトの型のプロパティ名のUnion型が 一致しない場合, 型エラーとなる.

T[K]によりkeyに対応する戻り値となる型を取得できる

function getProperty<T, K extends keyof T>(obj: T, keys: K) T[K] {
  return obj[key]
}

インデック型

  • オブジェクトのプロパティが可変の時, まとめて型定義ができる
type SupportVersions = {
  [env: number]: boolean
}

let versions: SupportVersions = {
  102: false,
  103: true,
  'v104': true // NG
}

readonlyとReadonly

  • readonly: クラスやオブジェクトに使う
  • const: 変数代入に使う
  • Readonly型に型エイリアスを指定するとすべてのプロパティがImmutableになる型が生成できる
type MuttableUser = {
  name: string
  gender: string
  age: number
}

type ImmutableUser = Readonly<MuttableUser>

unkonwn型

  • anyと同じくどのような型も代入できる
  • 代入された値のプロパティにアクセスできない
  • typeofやinstanceofと組み合わてunkonw型を具体化して突破する
  • unkonwnと型ガードの突破の組み合わせはanyより安全
const x: unkown = 123
console.log(x.toFixed(1)) // NG

// unkownによる型ガードを突破する
if (typeof x === 'number') {
  console.log(x.toFixed(1)) // 123.0
}

非同期関数

Async/Await

  • Promiseを使う
// Promise<T>
function fetchFromSensor(id: string): Promise<{ success: boolean }> {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ success: true })
    }, 100) // 100ms後に起動.
  })
}

// async/await
async function asyncFunc(): Promise<string> {
  const result = await fetchFromSensor('123') // { success: true }
  return `The result: ${result}`
}

// 非同期関数の即時実行
(async () => {
  const result = await asyncFunc()
  console.log(result)
})() // 即時関数

// Promiseとして扱う場合
asyncFunc().then((result) => console.log(result))
⚠️ **GitHub.com Fallback** ⚠️