typescript 是微軟發明的,像js是網景公司發明的,
每一門語言都是人發明的,因爲是人發明的,人老是會犯錯,
因此每一門語言都會有一些錯誤,那typescript主要就是爲了解決js的一些錯誤或認爲它很差的地方
複製代碼
TypeScript is a typed superset of JavaScript that compiles to plain JavaScript。javascript
TypeScript是一個編譯到純JS的有類型定義的JS超集html
TypeScript 可使用 JavaScript 中的全部代碼和編碼概念,TypeScript 是爲了使 JavaScript 的開發變得更加容易而建立的。例如,TypeScript 使用類型和接口等概念來描述正在使用的數據,這使開發人員可以快速檢測錯誤並調試應用程序前端
npm install typescript@2.9.2 -g
複製代碼
安裝完以後咱們會獲得兩個命令tsc和tsserverjava
tsc: TypeScript Compiler 把ts變成js的程序 node
npm install ts-node@7.0.0 -g
複製代碼
讓node支持typescriptwebpack
注意記下 ts-node 安裝後的可執行文件路徑,後面要用的 程序員
雖然須要多寫一些類型定義代碼,但TS在VSCode、WebStorm等IDE下能夠作到智能提示,智能感知bug,同時咱們項目經常使用的一些第三方類庫框架都有TS類型聲明web
聲明變量時須要指定類型 typescript
接口規定了對象中要包含的屬性(不能多也不能少) 數據庫
enum枚舉類型的屬性 只讀
拼寫錯誤
長期迭代維護的項目開發和維護的成員會有不少,團隊成員水平會有差別,而軟件具備熵的特質,長期迭代維護的項目總會遇到可維護性逐漸下降的問題,有了強類型約束和靜態檢查,以及智能IDE的幫助下,能夠下降軟件腐化的速度,提高可維護性,且在重構時,強類型和靜態類型檢查會幫上大忙,甚至有了類型定義,會不經意間增長重構的頻率(更安全、放心)
如今的SPA項目的不少bug都是因爲一些調用方和被調用方(如組件模塊間的協做、接口或函數的調用)的數據格式不匹配引發的,因爲TS有編譯期的靜態檢查,讓咱們的bug儘量消滅在編譯器,加上IDE有智能糾錯,編碼時就能提早感知bug的存在,咱們的線上運行時質量會更爲穩定可控
js七種數據類型 + any + enum + void + never
let isDone: boolean = false;
let decimal: number = 6;
let color: string = "blue";
// 數組,有兩種寫法
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
// 元組(Tuple)各元素的類型沒必要相同
let x: [string, number] = ["hello", 10];
// 枚舉
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;
// 不肯定的能夠先聲明爲any
let notSure: any = 4;
// 聲明沒有返回值
function warnUser(): void {
alert("This is my warning message");
}
let u: undefined = undefined;
let n: null = null;
// 類型永遠沒返回
function error(message: string): never {
throw new Error(message);
}
// 程序員自身 主觀判斷 h是一個字符串(這個判斷不必定對),ts會相信你,若是程序報錯就是程序員的鍋
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
let strLength: number = (someValue as string).length;
複製代碼
讓咱們既能享受js的靈活又能享受ts的類型檢查
// 若是沒有明確的指定類型,那麼 TypeScript 會依照類型推論(Type Inference)的規則推斷出一個類型。
let myFavoriteNumber = 'seven';
//等價於
let myFavoriteNumber :string= 'seven';
複製代碼
面向對象(oop)的三大特徵: 封裝,繼承,多態(抽象類, 接口, 泛型)
封裝: 隱藏內部實現細節,對外只暴露接口,調用方只需按接口規定調用便可,封裝的重要手段就是訪問控制權限,在ts中爲 public/protected/private
繼承:子類繼承父類,子類擁有父類中除了private聲明的全部屬性(建立子類時調用父類的構造方法)
多態: 多態是因爲繼承或實現而引出的,繼承者能夠重寫父類的方法, 實現者能夠實現接口中的抽象方法, 當用父類的引用指向子類的對象時,就造成了向上造型,從而造成了多態
TS中的類比js中的類多了 抽象類和訪問權限限制 兩個概念
JS面向對象編程的一個大問題就是沒有提供原生支持信息隱藏的方案(不少時候都是經過約定編碼方式來作)
私有的成員變量,公共的get/set方法
class Demo7 {
private _name:string;
private _age:number;
constructor(name: string, age: number) {
this._name = name;
this._age = age;
}
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
get age(): number {
return this._age;
}
set age(value: number) {
this._age = value;
}
}
複製代碼
TS中的一個核心原則之一就是類型檢查關注的是值的形狀,有時就叫作「鴨子辨型」(duck typing)或「結構化子類型」(structural subtyping)。TS中interface就承擔了這樣的角色,定義形狀與約束,在內部使用或者和外部系統協做
接口在定義的時候,不能初始化屬性以及方法,屬性不能進行初始化,方法不能實現方法體。 類實現接口以後,必須聲明接口中定義的屬性以及方法。
/** * 接口就是用代碼描述一個對象必須包含什麼屬性(包括方法), 不能多也不能少 */
interface Shape {
head: string,
body: string
}
interface Human {
name: string,
age: number,
shape: Shape,
say(word:string): void;
}
interface Man extends Human3{
gender:string
}
let h: Man = {
name: '張三',
gender: '男',
age: 27,
shape: {head: '頭', body: '身體'},
say(word: string): void {
console.log(word)
}
}
// 可選屬性
interface SquareConfig {
color?: string;
width?: number;
}
// 只讀屬性
interface Point {
readonly x: number;
readonly y: number;
}
複製代碼
類實現接口以後,必須聲明接口中定義的屬性以及方法,
interface Animal11 {
name: string;
eat: () => void;
}
class Dog implements Animal11 {
name: string = '小白';
eat() {
console.log('tag', 'I love eat bone!')
}
}
const dog: Dog = new Dog();
dog.eat();
複製代碼
數據庫中的一條記錄對應java中的一個實例對象,該類中的字段和字段類型與表中的字段及字段類型匹配, 有了TS的接口後,咱們能夠用 接口和 後端的實體類(或接口返回的數據) 一一對應,這樣就不會出現接口返回屬性找不到,或接口返回數據類型不匹配的問題
/** * Abstract class 抽象類(class可以擁有的 abstract class 都擁有) * * 抽象類中可能包含抽象方法(只有方法名,沒有具體實現的方法),因此抽象類不能夠實例化, 通常都做爲父類 * 反證法: 抽象類若是能夠實例化,實例化出來的對象如何執行抽象方法(沒有方法) * * class --> abstract class --> interface * 抽象程度逐漸提高,interface 抽象程度最高 */
abstract class Animal {
kind:string;
constructor(kind: string) {
this.kind = kind;
}
breath():void {
console.log('呼吸')
}
// Method 'birthType' cannot have an implementation because it is marked abstract
// 抽象方法不能有具體實現
abstract birthType():void
}
class Human1 extends Animal {
// 子類實現抽象父類中的抽象方法
birthType(): void {
console.log('胎生')
}
}
複製代碼
軟件工程中,咱們不只要建立一致的定義良好的API,同時也要考慮可重用性。 組件不只可以支持當前的數據類型,同時也能支持將來的數據類型
在像C#和Java這樣的語言中,可使用泛型來建立可重用的組件,一個組件能夠支持多種類型的數據。 這樣用戶就能夠以本身的數據類型來使用組件
總結一句話: 當咱們須要支持多種類型時,用T當佔位符,這個T就是泛型,表明普遍的類型
/** * 泛型就是用一個東西表示普遍的類型。能夠理解爲一個佔位符 */
function returnIt<T>(arg: T): T{
return arg;
}
let s = returnIt<string>('hi')
let n = returnIt<number>(123)
/** * 泛型接口 */
interface genFn<T> {
( arg : T ) : T;
};
let ide : genFn<number> = identity;
/** * 泛型類 */
class Gen <T> {
zero : T;
add : ( x : T, y : T ) => T;
}
let gen = new Gen<Number>();
/** * 泛型約束: 就是給泛型添加一些約束 * 例如: 並非任意的類型T的實例 都具有length屬性, 咱們先建立一個接口Lengthwise, 則T extends Lengthwise 時, T必定具有length屬性 */
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
複製代碼
T vs any
// T表明普遍的類型, 參數是T類型,返回值是T類型,一一對應
function f1<T>(arg:T):T {
return arg
}
let s1 = f1('hello')
s1.length;
// 若是使用any的話 沒法作類型推斷
function f2(arg:any):any {
return arg
}
let s2 = f2('hello')
複製代碼
其實數組接受的就是一個泛型
let ss:Array<string> = ['hello']
複製代碼
/** *你會發現用ts寫函數會很放心,由於我 入參類型寫死了,返回值類型也寫死了,惟一有可能出錯的地方就是個人邏輯代碼 * 也方便重構,n年以後回頭再看這個函數,一目瞭然, 若是用js的話,你可能要去差這個函數到底返回什麼類型的值 */
function add(a:number, b:number):number {
return a + b
}
複製代碼
/** * TypeScript裏的每一個函數參數都是必須的,而編譯器檢查用戶是否爲每一個參數都傳入了值 * JavaScript裏,每一個參數都是可選的,可傳可不傳。 沒傳參的時候,它的值就是undefined。 * * 在TypeScript裏咱們能夠在參數名旁使用 ?實現可選參數的功能 */
function hi2(name:string, age?:number) {
console.log(`Hi, ${name}, ${age}`)
}
hi2('張三', 27)
複製代碼
/** * 重載: 函數重載的意義在於可以讓你知道傳入不一樣的參數獲得不一樣的結果 * * 關於函數重載,必需要把精確的定義放在前面, * 最後函數實現時,須要使用 |操做符或者?操做符,把全部可能的輸入類型所有包含進去 */
// 上邊是聲明
function add1 (arg1: string, arg2: string): string;
function add1 (arg1: number, arg2: number): number;
function add1 (arg1: string | number, arg2: string | number) {
// 在實現上咱們要注意嚴格判斷兩個參數的類型是否相等,而不能簡單的寫一個 arg1 + arg2
if (typeof arg1 === 'string' && typeof arg2 === 'string') {
return arg1 + arg2
} else if (typeof arg1 === 'number' && typeof arg2 === 'number') {
return arg1 + arg2
}
}
// 參數要麼同時爲 number 要麼同時爲 string, 由於只定義了這兩種
// add1(1, '1')
複製代碼
重載和重寫有什麼區別?
因爲很是多的JavaScript庫並無提供本身關於TypeScript的聲明文件,致使TypeScript的使用者沒法享受這種庫帶來的類型,所以社區中就出現了一個項目DefinitelyTyped,他定義了目前市面上絕大多數的JavaScript庫的聲明,當人們下載JavaScript庫相關的@types聲明時,就能夠享受此庫相關的類型定義了
TypeScript有一個叫作聲明文件的功能, 即一個以.d.ts爲擴展名的文件,經過它咱們能夠爲JavaScript文件定義類型
.d.ts文件。 方法裏沒有實現細節,它只描述了類型。 TypeScript將.d.ts 和 .js組合到一塊兒,這樣,在編譯是使用這個聲明文件,在運行時使用這個原生的JS文件, 這樣咱們就能夠 使用Ts提供的類型安全和idea的智能語法提示
對於老項目,因爲TS兼容ES規範,因此能夠比較方便的升級現有的JS(這裏指ES6及以上)代碼,逐漸的加類型註解,漸進式加強代碼健壯性。遷移過程:
npm全局安裝typescript包,並在工程根目錄運行tsc --init,自動產生tsconfig.json文件。
初始化獲得的tsconfig.json無需修改,增長"allowJs": true選項。
配置webpack配置,增長ts的loader,如awesome-typescript-loader。
loaders: [
// All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
{ test: /\.tsx?$/, loader: "awesome-typescript-loader" }
]
複製代碼
此時你能夠寫文件名爲ts和tsx(React)後綴的代碼了,它能夠和現有的ES6代碼共存,VSCode會自動校驗這部分代碼,webpack打包也沒問題了。