TypeScriptまとめ集
目次
1.TypeScriptとは?
- altJS(JavaScriptの代替言語)の一種。
- altJSは他にもCoffeScript, Dart, Elm, ClojureScriptなどが存在する。
- TypeScriptはJavaScriptにはできなかった変数、関数の型宣言が可能。
- Angular, React, Vue.jsなどのJavaScriptフレームワークで標準採用されている。
2.型について
型の種類
基本となるプリミティブ型
型 | 説明 | 例 |
---|---|---|
number | 数値型(整数/浮動小数点数) | 1, -1, 0.5 |
string | 文字列型 | 'aiueo', "あいうえお" |
boolean | 真偽型(true/false) | true, false |
symbol | シンボル型 | Symbol('あいうえお') |
null | null | null |
undefined | undefined | undefined |
プリミティブ型以外の型
型 | 説明 | 例 |
---|---|---|
any | 任意の型 | 1, 'あいうえお', true |
unknown | 型が不明 | 1, 'あいうえお', true |
array | 配列型 | [1, 2, 3] |
tuple | タプル型(配列型の中でも値の型が統一されていないもの) | [1, 'AAA', true] |
enum | 列挙型 | 省略(後述の例文で紹介) |
function | 関数 | 省略(後述の例文で紹介) |
void | 戻り値なし | 省略(後述の例文で紹介) |
never | 絶対に起こらない | 無限ループ、常にエラーを吐き出すなど |
それ以外 | Date, RegExp, JSON, URLなど | 色々 |
- 型を省略した場合はany型扱いになる。
- any型とunknown型は似ているが、any型は各型のプロパティが使用できるのに対し、unknown型は使用できない。
- サイトによってプリミティブ型の定義が違っていたりするため、注意(これもあっているか微妙)。
typescript(プリミティブ型の型宣言)
//数値型
let num:number = 100;
//文字列型
let str:string = 'qiita';
//真偽型
let bool:boolean = true;
//シンボル型
let sym:symbol = Symbol('qiita');
//null
let nul:null = null;
//undefined
let und:undefined = undefined;
typescript(プリミティブ型以外の型宣言)
//any型(何でも代入できる、ただし基本は使わないこと)
let any1:any = 1;
any1.indexOf(1); //コンパイルは通るが、実行時エラー
//unknown型
let unknown1:unknown = 1;
unknown1.indexOf(1); //コンパイルエラー
//配列型
let array1:string[] = ['C#', 'Java', 'Ruby', 'Python'];
let array2:number[][] = [[10,20],[30,40],[50,60]];
let array3:Array<string> = ['C#', 'Java', 'Ruby', 'Python'];
let array4:Array<[number,number]> = [[10,20],[30,40],[50,60]];
//読み取り専用の配列
//ただし、readonlyキーワードがチェックするのは一次元目の要素のみ
let array5: readonly string[] = ['Java','Python','PHP','Ruby'];
let array6: ReadonlyArray<string> = ['Java','Python','PHP','Ruby'];
array3[1] = 'Python 3'; //結果:エラー
//連想配列
let dict1: { [index: string]: string } = {
'hoge': 'ほげ',
'foo': 'ふぅ',
'bar': 'ばぁ',
};
//タプル型 複数の異なる型の集合を表すためのデータ型
let tuple1: [string,number,boolean] = ['hoge',10.355,false];
//列挙型
//値を割り当てない場合は、上から0,1,2と値が振り分けられる
enum enum1{
MALE = 0,
FEMALE = 2,
OTHER = 4
}
let m:enum1 = enum1.OTHER;
console.log(m); //結果:4
console.log(enum1[m]); //結果:OTHER
//関数
function func1(arg1:string, arg2?:number=0){
return `${arg1}の値は${arg2}です。`
}
//void型
function void func2(arg1:string, arg2:number=0){
console.log('戻り値なし');
}
//never型
function func3(message: string): never {
throw new Error(message);
}
//それ以外
let date:Date = new Date();
型変換
- 型変換をしたい場合は、以下の2種類のやり方で変換できる
typescript(型変換)
let str:string = '100';
//<型名> で変換する
let num1:number = <number>str;
//as 型名 で変換する
let num2:number = str as number;
3.関数
関数の宣言方法
関数の宣言方法は
- function
- 関数リテラル
- アロー関数(ラムダ式)
の3種類がある。
typescript(関数の宣言方法)
//function
function triangle(base: number, height: number):number{
return base * height / 2;
}
//関数リテラル(2パターン)
let triangle = function(base: number, height: number): number {
return base * height / 2;
}
let triangle: (base: number, height: number) => number =
function (base: number, height: number): number {
return base * height / 2;
}
//アロー関数(ラムダ式)(2パターン)
let triangle = (base: number, height: number):number => {
return base * height / 2;
};
let triangle = (base: number, height: number):number => base * height / 2;
戻り値、引数について
- void, neverの使用方法について
- 引数を省略、既定値、可変調などの設定方法について
typescript(色々な関数)
//void 戻り値なし
function VoidFunc(name: string): void{
console.log(`こんにちは、${name}さん!`);
}
//never 決してありえない(常に例外を出す、無限ループとなっているなど、終点に到達できない)
function NeverFunc(): never {
throw new Error('Error is occured.');
}
//引数を省略可能
function OmitFunc(name?:string):string{
if(val === undefined){
return `こんにちは、名無しさん!`;
} else {
return `こんにちは、${name}`さん!;
}
}
//引数の既定値
function DefaultFunc(name:string='佐藤'):string{
return `こんにちは、${name}さん!`;
}
//可変調引数
function VariableFunc(...values: number[]){
let result = 0;
for(let value of values){
result += value;
}
return result;
}
オーバーロード
- オーバーロード...同じ名前でありながら、引数・戻り値の型が異なる関数を定義すること
typescript(オーバーロード)
//シグニチャだけの関数を準備
function show(value: number):void;
function show(value: boolean):void;
//オーバーロードした全ての型に対して有効となるように実装する
function show(value: any): void{
if(typeof value === 'number') {
...
} else {
...
}
}
4.高度な型について
共用型
「複数の値の中のどれか」を表す型
typescript(共用型)
//変数
let data: string | boolean;
data = 'hoge';
data = false;
//関数(戻り値)
function square(value: number): number | boolean {
if(value < 0){
return false;
} else {
return number + 1;
}
}
//関数(引数)
function show(value: string | number): void {
if(typeof value == 'string'){
console.log('string型です。');
} else {
console.log('number型です。');
}
}
型チェック
- typeofは、プリミティブ型の判定のみに有効
- instanceofはプリミティブ型以外の判定に有効
- inは、特定のメンバーが存在するかを判定
typescript(型チェック)
//typeof演算子
function show(value: string | number): void {
if(typeof value == 'string'){
console.log('string型です。');
} else {
console.log('number型です。');
}
}
//instanceof/in演算子
function show(value:Person):void{
//instanceof演算子を使う場合
if(value instanceof Person) { ... }
//in演算子を使う場合
//valueがnameプロパティを持つか
if('name' in value) { ... }
}
ユーザー定義の型チェック
- is演算子を使用することでユーザー定義の型チェック関数を自作できる
typescript(is演算子)
//戻り値がtrueであれば、引数がstring型であることを示している
function isString(test: any): test is string{
return typeof test === "string";
}
//isStringを使っての関数
function example(foo: any){
if(isString(foo)){
//fooはstringであることがis演算子よりわかる
//そのためlength(string型のメンバー)に安全にアクセスできる
console.log(foo.length);
}
}
null非許容型
TypeScriptでは、既定で全ての型に対してnull/undefinedを設定可能だが、
tsconfig.json内のstrictNullChecksオプションにtrueを設定することで、
全ての型でnull/undefinedの設定を禁止できる。
typescript(null非許容型)
let data1:string = null; //エラー
let data2:string = undefined; //エラー
//null/undefinedを設定できるようにしたい場合には...
let data1;string|null = null; //OK
let data2;string|undefined = undefined; //OK
?.演算子
- TypeScript3.7以降で使用可能に。
- 「?.」演算子は、null/undefinedでない時にアクセス可能。
typescript(?.演算子)
//通常の場合
let hoge:string | null | undefined;
let result = (hoge === null || hoge === undefined) ? undefined : hoge.trim();
//?.の場合
//null,undefinedでなければ、trimを行う
let result = hoge?.trim();
//連続でも指定可能
hoge?.foo?.bar();
「??」演算子
- TypeScript3.7以降で使用可能に。
- 「??」演算子は、null/undefinedである時に既定値を返す。
typescript(??演算子)
//通常の場合
console.log((hoge !== null && hoge !== undefined) ? hoge : '×');
//??演算子の場合
//null,undefinedの場合に既定値を設定する
console.log(hoge ?? '×');
型エイリアス
特定の型に対して別名(エイリアス)を設定する。
typescript(型エイリアス)
//タプル型にFooTypeという名前を付与
type FooType = [string, number, boolean];
//FooType型の変数dataを定義
let data:FooType = ['abc',100,true];
文字列リテラル型
特定の文字列(文字列リテラル)をそのまま型として利用できる。
typescript(文字列リテラル型)
//Season型を定義
type Season = 'spring'|'summer'|'autumn'|'winter';
//Season型ならコンパイルOK
//Season型以外ならコンパイルエラー となる
function getScene(s:Season){...}
getScene('spring'); //OK
getScene('fall'); //エラー
ジェネリック型
汎用的なクラス/メソッドに対して、特定の型を紐づけるための機能
typescript(ジェネリック型)
//ジェネリックを定義
//Tには任意の型を入れることができる
class MyGenerics<T>{
value!:T;
getValue():T{
return this.value;
}
}
let g = new MyGenerics<string>();
g.value = 'Hoge';
console.log(g.getValue()); //結果:Hoge
ちなみに
typescript(ジェネリック型)
//複数型引数を指定できる
class MyGenerics<T,R>{...}
//型引数の既定値を設定
class MyGenerics<T=string>{...}
//型引数の制限を設定
class MyGenerics<T extends Hoge>{...}
クラスだけでなくメソッドにもつけることが可能
typescript(ジェネリック型)
//ジェネリックメソッド
class MyCollection{
static addAll<T>(data:T[],...values:T[]):T[]{
return data.concat(values);
}
}
5.インターフェイスを使用した型定義
インターフェイスについては、モジュール化(未作成)を参考。
基本
typescript(インターフェイスによる型定義)
//インターフェイスで型を定義
interface Sample{
name:string; //プロパティシグニチャ
hello():string; //メソッドシグニチャ
}
//インターフェイスで定義した型の変数
let s:Sample{
name:'Taro',
hello(){
return `こんにちは、Qiitaさん!`;
}
}
※インターフェイスを定義せずに型情報を明示したい場合は、以下のようにできる(オブジェクト型リテラル)
typescript(オブジェクト型リテラル)
let s:{
name:string,
age:number
} = {
name:'Taro',
age:20
}
プロパティシグニチャ
- 「?」で省略可能なプロパティを表せる
- 「readonly」で読み取り専用プロパティを表せる
typescript(プロパティシグニチャ)
interface Sample{
readonly name:string;
age?:number;
}
コールシグニチャ
- 関数型を宣言するためのシグニチャ。
- 「(引数:型, ...):戻り値」の形式で宣言する。
typescript(コールシグニチャ)
interface hello {
(name:string):string
}
let h:hello = function(name:string):string{
return `こんにちは、Qiitaさん!`;
}
メソッドシグニチャ
- メソッドの型を宣言するためのシグニチャ。
- 「メソッド名(引数:型, ...):戻り値」の形式で宣言する。
typescript(メソッドシグニチャ)
interface Sample {
hello(name:string):string
}
let h:Sample = {
hello(name:string):string{
return `こんにちは、Qiitaさん!`;
}
}
インデックスシグニチャ
- 連想配列で使用すると便利
typescript(インデックスシグニチャ)
interface NumberAssoc{
[index:string]:Number
}
let list:NumberAssoc = {
'hundred': 100,
'thousand': 1000
};
コンストラクターシグニチャ
- コンストラクターの型を宣言できる。
- 「new(引数1, ...):クラス名」で定義できる。
typescript(コンストラクターシグニチャ)
//インターフェイス
interface Figure{
new(width:number, height:number):Triangle;
}
//継承先
class Triangle{
constructor(private width:number,private height:number){}
}
WeakType
- すべてのプロパティが省略可能(~?)である型のこと
- 全て省略した場合、もしくは全て入力した場合のみコンパイルが通るので注意。
typescript(WeakType)
//インターフェイス
interface MyOption{
name?:string;
timeout?:number;
}
let obj1:MyOptions = { name:'AAA'}; //エラー
let obj2:MyOptions = { }; //OK
let obj3:MyOptions = { name:'AAA', timeout:10}; //OK
keyof
既存の型から、型情報を切り出すことができる。
typescript()
interface Product{
name:string;
price:number;
}
type ProductKeys = keyof Product;
//これと同義
type ProductKeys = "name"|"price";
LookupTypes
指定の型から配下のプロパティ型を抜き出すことができる。
typescript(LookupTypes)
interface Product{
name:string;
price:number;
}
//Product型配下のプロパティ型を取得
type NameType = Product['name']; //string
type NamePriceType = Product['name'|'price']; //string|number
type HogeType = Product['hoge']; //エラー
Mapped Types
- 既存の型を変換するための仕組み。
- ユーティリティ型も似た仕組み(TODO:URL)
typescript(MappedTypes)
interface Product{
name:string;
price:number;
}
//Product型のプロパティセットから順にプロパティ名を取り出し、Kに割り当てなさいという意味
//[K in keyof Product]が該当箇所
type ReadonlyProduct = {
readonly [K in keyof Product]:Product[K]
;}
//これと同義
type ReadonlyProduct = {
readonly name:string,
readonly age:number
};
※ちなみに
- 「-?」で「?」を解除できる
- 「-readonly」で「readonly」を解除できる
typescript(MappedTypes)
interface ReadonlyProduct{
readonly name:string;
readonly price:number;
}
type Product = {
-readonly [K in keyof Product]:ReadonlyProduct[K]
;}
//これと同義
type Product = {
name:string,
age:number
};
ConditionalTypes(条件型)
与えられた条件を満たすかどうかによってX,Yのいずれかの型を選択する
三項演算子と似たような感じ。
typescript(ConditionalTypes)
//型Tが型Uに代入できなければnever型を返す
type Intersection<T, U> = T extends U ? T:never;