LoginSignup
0
1

[TypeScript] 型修飾子についてまとめた

Posted at

はじめに

今回は 型修飾子 についてまとめております。
前回のクラスに関する記事もよければご覧ください!

トップ型

全てのデータを割当できるデータ型です。

  • any
  • unknown

any

指定することでTypeScriptによる型チェックが行われなくなります。
型チェックの恩恵が失われてしまうので、なるべく利用しないようにしましょう。

function fn(input: number) {
     // NG: Property 'toUpperCase' does not exist on type 'number'.
     input.toUpperCase()
}

function anyFn(input: any) {
     // OK
     input.toUpperCase()
}

unknown

全てのオブジェクトを割り当てることが可能
anyとの違いは unknown型のメンバーに直接アクセスすることが禁止されているということです。

サンプルコードを見ていきましょう.

function fn(input: unknown) {
     // NG: 'input' is of type 'unknown'.
     input.toUpperCase();
}

inputに対して、toUpperCase()`を実行した際に型エラーが発生しています。

typeof, instanceofなどで対象の型を絞り込むことでメンバーへのアクセスが可能です。

function fn(input: unknown) {
     if(typeof input === "string") {
          input.toUpperCase();
     }
}

上記理由から、anyよりもunknownを優先的に利用しましょう。

ユーザー定義型ガード

TypeScriptでは、引数が特定のデータ型を返すか関数のためのユーザー定義型ガードが用意されています。

以下のサンプルコードを見ていきましょう。

function isString(input :unknown) {
    return typeof input === "string"
}

function toUpperCaseIsString(input: string | number) {
    if(isString(input)) {
        // NG: Property 'toUpperCase' does not exist on type 'string | number'.
        console.log(input.toUpperCase())
    }
}

input.toUpperCase()で型エラーが発生しています。

我々開発者はisString(input :unknown)が、引数がstring型であればtrue を返すことをすぐ認識できますが、TypeScriptはそうではありません。


以下のように関数パラメータの後に引数の戻り値を指定する必要があります。
これで型エラーが解消されます。

function isString(input :unknown): input is string {
    return typeof input === "string"
}

function toUpperCaseIsString(input: string | number) {
    if(isString(input)) {
        // OK
        console.log(input.toUpperCase())
    }
}

keyof

keyofはオブジェクトのキーの合併型を作成します。

以下のサンプルコードでは、keyfirst, second以外の値を取りうることができるため、型エラーが発生します。

interface ScoreObj {
    first: number
    second: number
}

function getScore(score: ScoreObj, key: string){
    //NG: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'ScoreObj'.
    console.log(score[key])
}

keyofを使用することで型エラーが解消されます。

interface ScoreObj {
    first: number
    second: number
}

function getScore(score: ScoreObj, key: keyof ScoreObj){
    console.log(score[key])
}

この場合、keyScoreObjキーの合併型("first" | "second")と評価されます。

typeof

typeofは与えられた値の型を返します。

const hasNameAge = {
    name: "taro",
    age: 30
}

let person: typeof hasNameAge;

上記の宣言は、以下のように宣言と同意義です。
コードがスッキリしますね。

let person: {
    name: string;
    age: number;
}

typeof + keyof

typeofkeyof を活用することで特定のオブジェクトのキーの合併型を簡単に定義できます。

let sampleObj = {
     value1: 100,
     value2: 500,
}

function getObjectValue(key: keyof typeof sampleObj) {
     console.log(sampleObj[key]);
}

//OK
getObjectValue("value1")
getObjectValue("value2")

//NG: Argument of type '"value3"' is not assignable to parameter of type '"value1" | "value2"'.
getObjectValue("value3")

型キャスト

型キャスト を利用することでTypeScriptによる型解釈を上書きすることが出来ます。

ユースケース

基本的に型キャストの利用は推奨されませんが、JSON.parseのようにトップ型(any)を返すメソッドがあります。
戻り値のデータ型を予め定義できる場合は、以下のように型キャストを使用します。

const data = `["hoge", "foo"]`;

// OK: any
let anyData = JSON.parse(data);

// OK: string[]
let stringArg = JSON.parse(data) as string[];

// OK: [string, string]
let StringTuple = JSON.parse(data) as [string, string];

非nullアサーション

変数の値として null, undefinedがあり得るものの、実際のデータはそうでないことが確証できることがあります。

データから null, undefinedを取り除く「!」がTypeScriptには用意されています。

const sampleMap = new Map([
    [1, "first"],
    [2, "second"]
])

sampleMap.get(2)!.toUpperCase();

sampleMap.get(2)string | undefinedですが、
sampleMap.get(2)!として実行することで値がstring型として上書きされています。

そのため、型エラーが発生することなく、toUpperCase() を実行できます。

const

TypeScriptには constアサーションが用意されており、指定した値を不変として扱います。
constアサーションはデータ型ごとに異なる振る舞いをします。

  • 配列
    • 配列をreadonly のタプル型として扱う
  • リテラル
    • 一般的なプリミティブ型ではなく、リテラル型として扱う
  • オブジェクトプロパティ
    • readonly として扱う

サンプルコードを見ていきましょう。

配列

// let args: string[]
let args = ["value1", "value2"];

// let argsAsConst: readonly ["value1", "value2"]
let argsAsConst = ["value1", "value2"] as const;

リテラル

// let one: number
let one = 1;

// let oneAsConst: 1
let oneAsConst = 1 as const;

オブジェクト

const sampleObj = {
    name: "Taro",
    age: 25
}

/*
const sampleObj: {
    name: string;
    age: number;
}
*/

const sampleObjAsConst = {
    name: "Taro",
    age: 25
} as const

/*
const sampleObjAsConst: {
    readonly name: "Taro";
    readonly age: 25;
}
*/

特定のライブラリなどで、フィールドが特定のリテラル型を求める場合があります。
その際は as constキーワードを使用するようにしましょう。

まとめ

以上です〜
次の記事ではジェネリック型についてまとめる予定です。

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