Open11

TypeScript の嫌いな理由が分かったかもしれない

山とコード山とコード

これでエラーが起きないことが気に食わないんだ、きっと。

class MountainEntity {
    id: number;
    name: string;

    constructor(
        id: number,
        name: string,
    ) {
        this.id = id;
        this.name = name;
    }
}

class Mountain {
    id: number;
    name: string;

    constructor(
        id: number,
        name: string,
    ) {
        this.id = id;
        this.name = name;
    }
}

const showMountain = (mountain: Mountain) => {
    console.log(mountain);
};

try {
    const mountain = new MountainEntity(1, '富士山');

    showMountain(mountain);
}
catch(ex) {
    console.error(ex);
}
山とコード山とコード

const mountain = new MountainEntity(1, '富士山'); で生成したのは、MountainEntity であって Mountain ではない。

showMountain 関数の引数は Mountain であるのにも関わらず、MountainEntity を引数に渡してもエラーにならない。

これ、生成されたクラスまで厳格にチェックにいけるのかな?

tsconfig とかで設定できないものなのかな?

山とコード山とコード

これは当然エラーが出る。

class MountainEntity {
    id: number;
    name: string;

    constructor(
        id: number,
        name: string,
    ) {
        this.id = id;
        this.name = name;
    }
}

class Mountain {
    id: string;
    name: string;

    constructor(
        id: string,
        name: string,
    ) {
        this.id = id;
        this.name = name;
    }
}

const showMountain = (mountain: Mountain) => {
    console.log(mountain);
};

try {
    const mountain = new MountainEntity(1, '富士山');

    showMountain(mountain);
}
catch(ex) {
    console.error(ex);
}

id が number と string で違うよってエラー。

Argument of type 'MountainEntity' is not assignable to parameter of type 'Mountain'.
  Types of property 'id' are incompatible.
  Type 'number' is not assignable to type 'string'.
山とコード山とコード

Rust で書くと、エラーになる。こうしてほしい。

main.rs
fn main() {
    let bar = Bar {
        id: 1,
        name: "test".to_string(),
    };

    show_foo(bar);
}

#[derive(Debug)]
struct Foo {
    id: i32,
    name: String,
}

#[derive(Debug)]
struct Bar {
    id: i32,
    name: String,
}

fn show_foo(foo: Foo) {
    println!("{:?}", foo);
}
error[E0308]: mismatched types
  --> src/main.rs:7:14
   |
7  |     show_foo(bar);
   |     -------- ^^^ expected struct `Foo`, found struct `Bar`
   |     |
   |     arguments to this function are incorrect
   |
note: function defined here
  --> src/main.rs:22:4
   |
22 | fn show_foo(foo: Foo) {
   |    ^^^^^^^^ --------
山とコード山とコード

これ、生成されたクラスまで厳格にチェックにいけるのかな?
tsconfig とかで設定できないものなのかな?

Qiita の記事を読む限り、なさそう?

山とコード山とコード

レイヤードアーキテクチャで実装しても、値を詰め替えることなくリターンできちゃうわけだ。

「言われてみれば、そりゃそうだ」って感じだけど、これは嫌だなぁ〜

山とコード山とコード

instanceof というアイデアがあった。

ただ、「コンパイル時のチェックの対象にはならない」らしい...

自分の目で確かめてみる

山とコード山とコード

コンパイル時にエラーにする書き方が思い付かない...

instanceof で処理を分岐することはできた。

class MountainEntity {
    id: number;
    name: string;

    constructor(
        id: number,
        name: string,
    ) {
        this.id = id;
        this.name = name;
    }
}

class Mountain {
    id: number;
    name: string;

    constructor(
        id: number,
        name: string,
    ) {
        this.id = id;
        this.name = name;
    }
}

const showMountain = (mountain: Mountain) => {
    if (mountain instanceof Mountain) {
        console.log(mountain);
        return;
    }
    console.log(mountain);
};

try {
    const mountain = new MountainEntity(1, '富士山');

    showMountain(mountain); // MountainEntity が出力される
}
catch(ex) {
    console.error(ex);
}