LoginSignup
0
0

Typescriptの応用的な使い方 @yukilulu0229

Last updated at Posted at 2024-02-23

AかつBのように定義する

type &

a.ts
type Engineer = {
  name: string,
  role: string
}

type Blogger = {
  name: string,
  follower: number
}

type EngineerBlogger = Engineer & Blogger

const yukilulu: EngineerBlogger = {
  name: "yukilulu",
  role: "engineer",
  follower: 1000
}
console.log(yukilulu)

Engineer & Blogger

&で繋げる

interface extends

a.ts
interface Engineer  {
  name: string,
  role: string
}

interface Blogger  {
  name: string,
  follower: number
}

interface EngineerBlogger extends Engineer, Blogger {}

const yukilulu: EngineerBlogger = {
  name: "yukilulu",
  role: "engineer",
  follower: 1000
}
console.log(yukilulu)

nterface EngineerBlogger extends Engineer, Blogger {}

extendsで繋げる

条件文で型を絞る

typeof 型で指定する

a.ts
function toUpperCaseFunc(x: string | number) {
  if (typeof x === "string") {
    return x.toUpperCase();
  }
  
  return "";
}
string | number

number型はtoUpperCase()のメソッドを使えないのでエラーになる

if (typeof x === "string") {
    return x.toUpperCase();
}

なので typeofでstring型のみにif文で分岐して利用できるようにする

in プロパティで指定する

b.ts
interface Engineer  {
  name: string,
  role: string
}

interface Blogger  {
  name: string,
  follower: number
}

type NomadWorker = Engineer | Blogger
function describeProfile(nomadWorker: NomadWorker) {
  console.log(nomadWorker.name); // 共通のnameだけが現在表示できる

  if ("role" in nomadWorker) {
    console.log(nomadWorker.role) // roleを持っているEngineerの型に絞り込まれた
  }

  if ("follower" in nomadWorker) {
    console.log(nomadWorker.follower) // followerを持っているBloggerの型に絞り込まれた
  }
}

console.log(nomadWorker.name); // 共通のnameだけが現在表示できる

この時点ではEngineerかBloggerとどちらなの状態かわからないので共通のnameのみを使える

if ("role" in nomadWorker) {
  console.log(nomadWorker.role) // roleを持っているEngineerの型に絞り込まれた
}

"role" in nomadWorkerをつかってroleを持っているEngineerの型に絞り込まれた

if ("follower" in nomadWorker) {
    console.log(nomadWorker.follower) // followerを持っているBloggerの型に絞り込まれた
}

"follower" in nomadWorkerを使ってfollowerを持っているBloggerの型に絞り込まれた

instanceof インスタンスで絞り込む

c.ts
class Dog {
  speak() {
    console.log("bow-bow")
  }
}

class Bird {
  speak() {
    console.log("tweet-tweet")
  }
  fly() {
    console.log("flutter")
  }
}

type Pet = Dog | Bird

function havePet(pet: Pet) {
  pet.speak(); // 共通のspeakはそのまま使える
  if (pet instanceof Bird) {
    pet.fly(); // petはBirdのインスタンスであるという指定しからBirdの型であると絞り込まれた
  }
}

havePet(new Bird());
pet.speak()

// 共通のspeakはそのまま使える

if (pet instanceof Bird) {

pet instanceof Bird petはBirdのインスタンスであるという指定しからBirdの型であると絞り込まれた

タグ付きユニオンで分岐させる kind

リテラル型をうまく利用する

kind:"dog" = "dog"

a.ts
class Dog {
  kind:"dog" = "dog"
  speak() {
    console.log("bow-bow");
  }
  sit() {
    console.log("sit")
  }
}

class Bird {
  kind:"bird" = "bird"
  speak() {
    console.log("tweet-tweet");
  }
  fly() {
    console.log("flutter");
  }
}

type Pet = Dog | Bird;

function havePet(pet: Pet) {
  pet.speak(); // 共通のspeakはそのまま使える
  switch (pet.kind) { // kindで分岐させる
    case "dog":
      pet.sit();
      break
    case "bird":
      pet.fly()
      break
  }
}

havePet(new Bird());
kind:"dog" = "dog"

kind: "文字列a" = "文字列a"でkindの中に入る文字列を固定する

switch (pet.kind) { // kindで分岐させる
    case "dog":
      pet.sit();
      break
    case "bird":
      pet.fly()
      break
}

それをkindを使ってswitch文で分岐させる

interfaceでの書き方

kind: "engineer"

型アサーションを使って上書きする

.ts
const input = document.getElementById("input");
input.value = "init input name";

そのままだと HTMLElementといういう型になって valueを使うことはできない

後ろに as をつける

Reactを使う場合を後ろにasをつけるほうがよい
理由はタグが紛らわしくなるから

as.ts
const input = document.getElementById("input") as HTMLInputElement;
input.value = "init input name";

as HTMLInputElementを後ろにつけて HTMLInputElement型だと上書きする

変数を分けないで書く

.ts
(document.getElementById("input") as  HTMLInputElement).value = "init value"

()で囲ってその後ろにプロパティをつける

先頭に <> をつける

.ts
const input = <HTMLInputElement>document.getElementById("input");
input.value = "init input name";

<HTMLInputElement>をつけて HTMLInputElement型とする

関数のオーバーロード

.ts
function toUpperCaseFunc(x: string): string;
function toUpperCaseFunc(x: number): number;
function toUpperCaseFunc(x: string | number) {
  if (typeof x === "string") {
    return x.toUpperCase();
  }

  return x;
}

const val = toUpperCaseFunc(3) // number型になっている
console.log(val)
const val2 = toUpperCaseFunc("hello") // string型になっている
console.log(val2)
function toUpperCaseFunc(x: string): string;
function toUpperCaseFunc(x: number): number;
function toUpperCaseFunc(x: string | number) {
  if (typeof x === "string") {
    return x.toUpperCase();
  }

  return x;
}

function toUpperCaseFunc(x: string): string;
function toUpperCaseFunc(x: number): number;
それぞれを関数の上に書く
それによってしっかりした型で帰るようになる

オプショナルチェイン ?

.ts
interface DownloadData {
  id: number,
  user?: {
    name: string
  }
}

const downloadData:DownloadData = {
  id: 1
}
console.log(downloadData.user?.name)
user?: {
    name: string
}

userがundefinedであることを許容している場合

console.log(downloadData.user?.name)

downloadData.user?.name
?をつけることでなかったらundefinedを返す あれば そのまま返すと設定することができる

nullish coalecing ??

.ts
const userData = downloadData.user ?? "no-user"

??をつけることで undefined または null のときに 後ろのものが代入される

注意 OR演算子だと起こり得る事

.ts
const userData2 = downloadData || "no-user"

|| これだと 0 や false というデータが入っていても "no-user"が入ってしまうことに注意

オブジェクトのメンバーの型を取得するlookup型

参考:

.ts
type Response = {
  users: {
    id: number
    name: string
    email: string
  }[]
  products: {
    id: number
    name: string
    price: number
  }[]
}

type Users = Response['users']
type User = Users[0]
type Products = Response['products']
type Product = Products[0]

const user: User = { id: 1, name: 'Alice', email: 'alice@example.com' }
const product: Product = { id: 1, name: 'Apple', price: 100 }
type Users = Response['users']
type User = Users[0]
type Products = Response['products']
type Product = Products[0]

これで抽出して定義することができるようになる

書き方は []配列で書く

interfaceの関数のオーバーロード

.ts
interface TmpFunc {
  (a: string): number,
  (a: number): number
}

const tmpFunc: TmpFunc = function (a: number | string) {
  console.log(a);
  return 0
}

(a: number | string)|でわける

型の互換性

インターセクションのオーバーロード

.ts
interface FuncA {
  (a: string, b: number): number,
  (a: number, b:string): number
}

interface FuncB {
  (a: string): number
}

let intersectionFunc: FuncA & FuncB;
intersectionFunc = function (a: number | string, b?: string | number) {
  console.log(`${a}: ${b} `)
  return 0
}
let intersectionFunc: FuncA & FuncB;

&で繋げる


intersectionFunc = function (a: number | string, b?: string | number) {
  console.log(`${a}: ${b} `)
  return 0
}
  • |で繋げる
  • b?: string | numberはFuncBで使われていない可能性があるので ?を使う必要がある

レストパラメーターに型を指定する

レストパラメーターとは引数などに何個で入れられるようにする書き方
...argのように書く

配列の中身がすべて同じ型

.ts
function addvancedFn(...arg: number[]):void {
  console.log(arg)
}

addvancedFn(1, 2, 3)

...arg: number[]ですべて同じ方になる

配列の中身が必ず個数が一致しなくてもよい書き方

.ts
function addvancedFn(...arg: [string, number, boolean?]):void {
  console.log(arg)
}

addvancedFn("1", 2)

...arg: [string, number, boolean?]

?をつけてなくてもいい状態にする

必ず後ろから?をつける
前からは付けれない

配列の途中まで型をしてあとはレストパラメーターのように使う

.ts
function addvancedFn(...arg: [string, number, boolean, ...number[]]):void {
  console.log(arg)
}

addvancedFn("1", 2, false, 2 ,3 ,4, 5)

...number[] 一番うしろに配列をつける

配列やタプルをreadonlyにする

配列

readonlyなのでpushなどの変更もできなくなる

.ts
function addvancedFn(...arg: readonly number[]):void {
  console.log(arg)
  // arg.push("A");
}

addvancedFn(1, 2, 3, 4)

...arg: readonly number[] argの後ろに配列の前につける

タプル

.ts
function addvancedFn(...arg: readonly [string, number, boolean, ...number[]]):void {
  console.log(arg)
  // arg.push("A");
}

addvancedFn("1", 2, false, 2 ,3 ,4, 5)

...arg: readonly argの後ろ 配列の前につける

使わないほうがいい

インデックスシグネチャ オブジェクトに追加できるようにする機能

.ts
type MovietheatreSeatingAssigment = {
  [seatNUmber: string]: string;
};

非nullアサーション nullを許容しないを簡潔に書く !

参考(@k_kind ):

.ts
const inputElement = document.getElementById('input')!
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