typescriptとは
typescript = type+(java)script=>javascriptにtypeという機能が追加! typescriptはjavascriptのsuperset
javascriptとtypescriptの最大の違いは、typeの提供有無かと思います。
typeを使用しないjavascriptは動的、typeを使用するtypescriptは静的!
動的という特性により、プロパティの変更が非常に簡単であると長所と同時に、変更が容易・柔軟であるため、予期しない動作をするかもです。
静的は、初期はコストがかかりますが、安全性を担保できるかと思います!
また、JavaScriptはinterpreter言語で、compile過程を通さないためruntimeでエラーを発見!
typescriptはcompile言語であり、compile段階でエラーを発見することができます。
そのため、早い段階でエラーに対応することができます。
実際、ある研究によると、javascriptバグの15%が事前にtypescriptで感知できるそうです。
指定により協業にも!!
このようにバグを未然に予防する・type指定による直感的にコード
type指定方法
// javascript
let age = 10;
let month = 10;
このように変数というものに名前を付ける方法によってデータを説明できるようになりますが、これがjavascriptです。
// javascript
let weight = 80;
// typescript
let weight:number = 80;
javascript観点では「重さだな」と解釈できるはずですが、weightを使う状況になった時、weightに入っている値が数字なのか文字なのか、あるいは別のtypeの値なのかはわかりません。
なぜなら? javascriptコードではそれを説明していません。ただ意味だけが分かります。
この足りないところをtypescriptが埋めてくれるのです。weightはnumber typeを指定することによりはるかに明示的かつ明確に、誰が見ても解釈の余地がなく説明しています。
ここで、typdscriptはさらに一段階進みます。
let height:number = 178;
178だから身長なんだと分かります。ところで、ここで178の単位は何だろう? 普段、身長だと言えば単位を使うのに(cm,inch)、これがアメリカ人の身長なのか日本人の身長なのかコードだけでは、わかりません。
typescriptでは、type aliasを利用して新しいタイプを定義することができます。
type Centimeter = number
let height:Centimeter = 178;
let weight:number = 80;
heightにnumberのtypeを使用するのではなく、新たに作ったCentimerというtypeを使用します。
numberのタイプに私たちがどんな意味を付与した表現をしたものです。
身長は178というnumberでCentimerという単位を使用する!ってことをコードを見て知ることができます。
あそこの上にlet weight:number=80;
はkgかpoundかcommentを書いていない以上、知ることができません。(提供される情報が制限的)
このようにtype定義だけでもはるかに強化された表現が可能となります!
type RainbowColor = 'red'|'orange'|'yellow'|'green'|'blue'|'indigo'|'violet'|
let color: string = 'orange' // 情報が制限的
let color1: RainbowColor = 'orange' // 明確、豊富
color1 = 'black' // occured error
export type FooFunction = () => string; // function type定義
const foo: FooFunction = function() {
return 'fooFunction'
}
カラーなのに、これがすごくカラーなんだ! 虹のカラーだけ入るんだ! ということがわかります。
color1 = black
の場合は、エラーを出します。color1はRainbowColorTypeですが、blackはRainbowColorTypeじゃないからです。プログラムを実行する前にエラーがわかるため、バグも減るし、早めに対応ができます。より安心して開発することができます。
JavaScriptの場合は、そのまま入ります。なぜか? タイプ規定がないから!
// 定義 開始
export interface User {
readonly id: number;
readonly name: Name;
email: string;
receiveInfo: boolean;
active: YesOrNo;
}
export interface User {
address?: string;
}
export interface NewsStore {
(index: number) => NewsFeed[];
}
// 定義 終了
// 使用 開始
const aUser: User = {
id: 1;
readonly name: 'jung';
email: 'jung@gmail.com';
receiveInfo: false;
active: 'Y';
address?: 'japan';
}
const news: NewsStore = function(index: number){
return newsFeed[];
}
mixin
mixinはクラスを利用して継承を実現しますが、classのextendsを利用せず、クラスをまるで関数のように、あるいは単独のオブジェクトのように扱いながら、必要な場合によってクラスを合成して新しい機能に拡張していく技法です! クラス自体を独立した主体として見ています。そのため、コード自体ではクラス間の上下関係が描写されていません。使用する理由は、多重継承を実現したい時!
class Api {
protected getRequest<AjaxResponse>(): AjaxResponse {
this.ajax.open('GET', url, false);
this.ajax.send();
return JSON.parse(this.ajax.response);
}
}
class NewsFeedApi extends Api{
getData(): NewsFeed[] {
return this.getRequest<NewsFeed[]>();
}
}
class NewsDetailApi extends Api{
getData(): NewsDetail {
return this.getRequest<NewsDetail>();
}
}
class Api {
getRequest<AjaxResponse>(url: string): AjaxResponse {
const ajax: XMLHttpRequest= new XMLHttpRequest();
ajax.open('GET', url, false);
ajax.send();
return JSON.parse(ajax.response) as AjaxResponse;
}
}
class NewsFeedApi {
getData(): NewsFeed[] {
return this.getRequest<NewsFeed[]>(NEWS_URL);
}
}
class NewsDetailApi {
getData(): NewsDetail {
return this.getRequest<NewsDetail>(NEWS_URL);
}
}
/**
* @param targetClass
* @param baseClasses
*/
function applyApiMixins(targetClass: any, baseClasses: any[]): void {
baseClasses.forEach(baseClass => {
Object.getOwnPropertyNames(baseClass.prototype).forEach(name => {
const descriptor = Object.getOwnPropertyDescriptor(baseClass.prototype, name);
if (descriptor) {
Object.defineProperty(targetClass.prototype, name, descriptor);
}
});
});
}
// (targetClass, 継承したいクラスのため、配列型)
applyApiMixins(NewsFeedApi, [Api]);
applyApiMixins(NewsDetailApi, [Api]);
// ここまでだと、this.getRequestでエラーが発生!!
// コード上では、apiクラスとnewFeedApiが何の関係も結んでいないし
// mixin関数にてこの二つのクラスが機能が合成されることまで追跡できません。
// そのため、typescript complierに「この二つのクラスは合成されるよ!」をいう事実を
// 知らせる必要があります
interface NewsFeedApi extends Api {};
interface NewsDetailApi extends Api {};
type定義
type定義、データを説明するのはなぜ重要かとすると、 front-endコードの構成要素を見ると、データが占める割合がとても多いです。front-endアプリの規模が大きくなればするほど扱うデータは多くなります。そしてそのデータは状況によって何らかの理由で変わるケースがあります。規模が大きければ大きいほど参画する開発者の数も多くなり、サービスで使用されるアプリケーションは一度作られて終わりではなく、サービスが終了するまで数年間メンテナンスしなければならない特性があります。ということは、開発者も変わり続ける可能性があるということです。そうすると、data typeの説明が明確でなければ、dataを修正するために多くの文書や様々なものを探し回らなければならない問題が生じ、バグを作ってしまう可能性が高くなります。
それでコード自体がdata typeをよく説明できると言えば最も良いだろうし、javascriptはコンセプト上構造的にデータタイプを説明するのが難しい言語であるため、データタイプを説明できるtypescriptという言語が注目されています。