今更TypeScriptをやる ぱーと2
前回は下の
これの続きをやっていく。
Interfaces
ダックタイピングとかストラクチャルサブタイピングとかのやつ。 JavaとかのInterfaceと違って、明示的に継承とか実装?(implements だっけ?)とかしてなくても、メンバがアレばよしなに動くよ〜みたいな感じ。
interface Test { a: string; b: () => string; } const f = (t: Test) => { console.log(t.a); console.log(t.b()); } f({a: '', b: ()=>('a')}); f({a: 1, b: ()=>('a')}); // エラー。 aはstringでないのでだめ。 f({a: '', b: ()=>(2)}); // エラー。 bの返り値がstringではないのでだめ。
基本はこういう感じかな。
Optional Properties
オプションなやつ。あってもなくてもいいよ〜っていうあれっすね。
interface Test { a?: string; b?: number; } const test = (t: Test) => { if (t.a) { console.log(t.a); } if (t.b) { console.log(t.b); } } test({ a: '' }); test({ b: 1 }); test({}); test({ b: ''}); // エラー。bがnumberではない。
なくてもよいよ〜とできる。
Readonly properties
そのままですね。 読み取り専用。 各種viewのフレームワーク?ライブラリのプロパティとかにつけるといいのかな?
interface Test { readonly a: string; b: number; } const test = (t: Test) => { t.a = ''; // エラー。読み取り専用なので代入できんよ〜。 t.b = 1; }
便利〜
ReadonlyArray
とかもあるらしい。
Array
を読み取り専用にするものらしい。ほかにも ReadonlyMap
とか ReadonlySet
とかがある様子。
Excess Property Checks
interface Test { a?: string; b?: number; } const test = (t: Test) => { if (t.a) { console.log(t.a); } if (t.b) { console.log(t.b); } } test({ a: '', c: 1}); // エラー。cがあるので型がアレ。
みたいなので c
とか他のプロパティもいい感じに許容したいよ〜みたいなアレ。
interface Test { a?: string; b?: number; [propName: string]: any; }
こうするといい感じになるとかなんとか。 あんまりよくない気がする。
interface Test { a?: string; b?: number; } const test = (t: Test) => { if (t.a) { console.log(t.a); } if (t.b) { console.log(t.b); } } const t = { a: '', c: 1}; test(t);
これでどうにかなるし、functionの引数を直で書くときはそりゃその引数の型に厳格でいけよ。というだけな気がするし、まあはい。 こういう風に任意のプロパティをアレできるよ〜っていうのは覚えておいたほうが良いねというのはある。
Function Types
functionの型をあれするinterfaceをかくこともできる。
interface Test { (a: string): number; } interface Test2<T> { (a: T): T; } let a: Test = (b) => { return 1; } a = () => (''); // エラー
引数のところでエラーが出てないけどなんでだろ?
JS、引数渡さなかったり、多く渡してもfunctionを実行できたりするからそれ関係でなんかあれなのかな?
arguments
とかどうなるんだろ。
interface Test { (a: string): number; } interface Test2<T> { (a: T): T; } let a: Test = function () { const b = arguments[0]; return b.length; } a('a');
any
になったわ。まあそうねという感じ。
Indexable Types
Excess Property Checks
のところで出てきた [hoge: string]: string
みたいなやつね。
interface Test { [index: number]: string; } const test = (t: Test) => { t[0] = 'asdf'; t[4] = 'asdf'; t[5] = 1; // エラー。stringではないから。 };
はい。
index
のところの型は string
か number
のみです。
複数定義することも可能。
interface Test { [index: number]: string; [index: string]: string; }
ただし、値の型は一致させておく必要があります。
なぜならJSの挙動で、 t[0]
と t['0']
が同じものを指すので型を違えることはできません。
ベースがJSであるゆえの問題というかなんというか。
まあこんな使い方をすることがなさそうだからなんとアレだけどね。
あと、index
にstring
を使った場合は他のプロパティもそれの型に合わせる必要があります。
interface Test { [index: string]: string; x: number; // エラー。stringにしないといけない。 }
理由は上のアレと似たようが話ですね。 t.x
と t['x']
は同じものを指すので、型を違えることはできないわけですね。
Class Types
クラスに実装(implements)をするぞ〜というアレ。
interface Test { a: number; } class MyClass implements Test { a = 1; b = 2; } const myClass = new MyClass(); const test = (t: Test) => { return t.a; }; console.log(test(myClass));
こんな感じっすね。
MyClass
の a
を定義しないとエラーになる。
コンストラクタのあれこれもかけるとかなんとか。
interface TestConstructor { new (a: number); } interface Test { a: number; } class MyClass implements Test, TestConstructor { // エラー a = 1; b = 2; constructor(a: number){ this.a = a; } }
こういうふう。 new
っていうのを使う。
でこれはエラーになる。
implements
は インスタンスの型のチェックになるため、対象のクラスに実装しようとすると変になる。
のでうまくやるには以下のようにする。
interface TestConstructor { new (a: number): Test; } interface Test { a: number; } class MyClass implements Test { a = 1; b = 2; constructor(a: number){ this.a = a; } } const createMyClass = (t: TestConstructor, a: number) => { return new t(a); } const test = (t: Test) => { return t.a; }; console.log(test(createMyClass(MyClass, 1)));
こういう感じで別にインスタンスを作るfunctionを定義してよしなに使う感じっぽい。
interface TestConstructor { new (a: number): Test; } interface Test { a: number; } const MyClass: TestConstructor = class implements Test { a = 1; b = 2; constructor(a: number){ this.a = a; } } const test = (t: Test) => { return t.a; }; const a = new MyClass(1); console.log(test(a));
こういうふうでも可。
ただ、new
でインスタンスにしたものの型がnew
のところに書いた型になってしまうので、いまいちアレかなぁとおもった。
Extending Interfaces
まあ interface の継承ですね。
interface Test1 { a: number; } interface Test2 { b: string; } interface Test3 extends Test1, Test2 { c (d: number): number; } const test = (t: Test3) => { t.a; t.b; t.c(1); }
こういう感じ。
Hybrid Types
Function Types
といろいろ合体するやつ。
interface Test { (a: number): number; a: number; b: string; } const test: Test = (a) => { return a; } test.a = 1; test.b = '';
こういう感じ。
ただ、 test
のa
とかが後で追加みたいな感じでちょっとイケてないので、 getTest
みたいなのをなんか作ってまとめちゃうとかがいいっぽい。
interface Test { (a: number): number; a: number; b: string; } const getTest = (a: number, b: number): Test => { const test: Test = (a) => { return a; } test.a = 1; test.b = ''; return test; }
こういう感じ。
Interfaces Extending Classes
クラスをinterface
として扱えるという感じなのかな?
class Test1 { private a = 1; add(b: number): number { return b + 1; } } interface Test2 extends Test1 { sub(c: number): number; } class Test3 extends Test1 implements Test2 { sub(c) { return c - 2; } } class Test4 implements Test2 { // エラー。 private の a が無いという感じ。 add(b) { return b; } sub(c) { return c; } }
private
のプロパティとかを作ると、 interface
として扱った場合に、private
の宣言がアレするので死ぬ感じっぽい。
なので、使い所はよくよく考えたほうが良い様子。
はい
ガバーっとみた。 なるほどな〜というのと、これは使わないだろうなぁ…というのがあったりした。
また引き続きやっていく。