TypeScript 入門自學筆記(一)

碼文不易,轉載請帶上本文連接,感謝~ https://www.cnblogs.com/echoyya/p/14542005.html

TypeScript 介紹

首先介紹一下什麼是TypeScript ,與JavaScript的區別,及優缺點javascript

什麼是TypeScript?

  1. 是添加了類型系統的 JavaScript,適用於任何規模的項目。
  2. 是一門靜態類型、弱類型的語言。
  3. 徹底兼容 JavaScript,且不會修改 JavaScript 運行時的特性。
  4. 能夠編譯爲 JavaScript,而後運行在瀏覽器、Node.js 等任何能運行 JavaScript 的環境中。
  5. 擁有不少編譯選項,類型檢查的嚴格程度可經過配置文件決定。
  6. 能夠和 JavaScript 共存,這意味着 JavaScript 項目可以漸進式的遷移到 TypeScript。
  7. 加強了編輯器(IDE)的功能,提供了代碼補全、接口提示、跳轉到定義、代碼重構等能力。
  8. 擁有活躍的社區,大多數經常使用的第三方庫都提供了類型聲明,而且開源免費

JavaScript 的缺點

首先JavaScript 是一門很是靈活的編程語言:html

  • 它沒有類型約束,一個變量可能初始化時是字符串,又被賦值爲數字。
  • 因爲隱式類型轉換的存在,有些變量的類型很難在運行前就肯定。
  • 基於原型的面向對象編程,使得原型上的屬性或方法能夠在運行時被修改。

TypeScript 的類型系統,在很大程度上彌補了 JavaScript 的缺點。前端

爲何使用 TypeScript?

通常來講,在大型項目中,後期維護成本比前期開發成本要多得多,因此團隊規範化尤其重要,包括編碼規範,方法調用規範等,而TS能夠經過代碼的方式,約束團隊開發,這樣纔有利於後期維護及擴展,從而達到高效的開發java

兩個最重要的特性——類型系統適用於任何規模es6

優點:強大的IDE支持,支持類型檢測,容許爲變量指定類型,語法檢測,語法提示web

缺點:有必定的學習成本,須要理解 接口,泛型,類,枚舉類型等前端可能不是很熟悉的知識點。typescript

接口(Interfaces):能夠用於對``對象的形狀Shape`進行描述npm

泛型(Generics):在定義函數,接口或類時,不預先指定具體的類型,而是在使用時在指定類型的一種特性編程

類(Classes):定義了一件事物的抽象特色,包括屬性和方法json

安裝

若想使用TS進行開發,首先必需要搭建搭建TypeScript開發環境
安裝: npm install -g typescript,全局安裝,能夠在任意位置執行tsc

版本:tsc -v

編譯:tsc 文件名.ts

TS 中,使用:爲變量指定類型,: 先後的空格無關緊要。TS 只會在編譯時對類型進行靜態檢查,如發現有錯誤,編譯時就會報錯。而在運行時,與普通的 JavaScript 文件同樣,不會對類型進行檢查。

編譯時即便報錯,仍是會生成編譯結果,仍然可使用編譯以後的文件,若想在報錯時終止 js文件的生成,能夠在 tsconfig.json 中配置 noEmitOnError 便可。

TypeScript 的特性

類型系統按照類型檢查的時機分類,能夠分爲動態類型靜態類型

類型系統

TypeScript 是靜態類型

動態類型:是指在運行時纔會進行類型檢查,類型錯誤每每會致使運行時錯誤。JavaScript 是一門解釋型語言,沒有編譯階段,因此它是動態類型,如下代碼在運行時纔會報錯:

// test.js
let foo = 1;
foo.split(' ');

// TypeError: foo.split is not a function  運行時會報錯(foo.split 不是一個函數)

靜態類型:是指編譯階段就能肯定每一個變量的類型,類型錯誤每每會致使語法錯誤。TypeScript 在運行前須要先編譯爲 JavaScript,而在編譯階段就會進行類型檢查,因此 TypeScript 是靜態類型,如下代碼在編譯階段就會報錯:

// test.ts
let foo: number = 1;
foo.split(' ');

// Property 'split' does not exist on type 'number'. 編譯時報錯(數字沒有 split 方法),沒法經過編譯

TypeScript 是弱類型

類型系統按照是否容許隱式類型轉換分類,能夠分爲強類型弱類型

如下代碼在 JS或 TS 中均可以正常運行,運行時數字 1 會被隱式類型轉換爲字符串 '1',加號 + 被識別爲字符串拼接,因此打印出結果是字符串 '11'。

console.log(1 + '1');   // 11

TS 是徹底兼容 JS的,並不會修改 JS 運行時的特性,因此它們都是弱類型。雖然 TS 不限制加號兩側的類型,可是能夠藉助類型系統,以及 ESLint 代碼檢查,來限制加號兩側必須同爲數字或同爲字符串。會在必定程度上使得 TypeScript 向強類型更近一步了——固然,這種限制是可選的。

這樣的類型系統體現了 TypeScript 的核心設計理念:在完整保留 JavaScript 運行時行爲的基礎上,經過引入靜態類型系統來提升代碼的可維護性,減小可能出現的 bug。

原始數據類型基本使用

布爾值、數值、字符串、null、undefined,爲變量指定類型,且變量值需與類型一致

let flag: boolean = false
let num: number = 15
let str: string = 'abc'
var a: null = null
var b: undefined = undefined

// 編譯經過

使用構造函數創造的對象不是原始數據類型,事實上 new XXX() 返回的是一個 XXX對象

let flag:boolean=new Boolean(false)  // Type 'Boolean' is not assignable to type 'boolean'.
let num: number = new Number(15)     // Type 'Number' is not assignable to type 'number'.
let str: string = new String('abc')  // Type 'String' is not assignable to type 'string'.

// 編譯經過

可是能夠直接調用 XXX 也能夠返回一個 xxx 類型

let flag: boolean = Boolean(false)
  let num : number  = Number(15)
  let str : string  = String('abc')
  
  // 編譯經過

數值

使用 number 定義數值類型:

let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;  // ES6 中的二進制表示法
let octalLiteral: number = 0o744;    // ES6 中的八進制表示法
let notANumber: number = NaN;
let infinityNumber: number = Infinity;

編譯結果:

var decLiteral = 6;
var hexLiteral = 0xf00d;
var binaryLiteral = 10; // ES6 中的二進制表示法
var octalLiteral = 484; // ES6 中的八進制表示法
var notANumber = NaN;
var infinityNumber = Infinity;

ES6 中二進制和八進制數值表示法:分別用前綴0b|0B0o|0O表示。會被編譯爲十進制數字。

字符串

使用string定義字符串類型:

let myName: string = 'Echoyya';
let str: string = `Hello, my name is ${myName}.`; // 模板字符串

編譯結果:

var myName = 'Echoyya';
var str = "Hello, my name is " + myName + "."; // 模板字符串

ES6 中模板字符串:加強版的字符串,用反引號(`) 標識 ${expr} 用來在模板字符串中嵌入表達式。

空值 及(與Null 和 Undefined的區別)

JavaScript 沒有空值(Void)的概念,在 TS中,用 void 表示沒有任何返回值的函數:

function alertName(): void {
    alert('My name is Tom');
}

然而聲明一個 void 類型的變量沒什麼用,由於只能將其賦值爲 undefined 和 null:

let unusable: void = undefined;

Null 和 Undefined

let u: undefined = undefined;
let n: null = null;

區別:undefined 和 null 是全部類型的子類型。也就是說 undefined 類型的變量,能夠賦值給全部類型的變量,包括 void 類型:

let num: number = undefined;

let u: undefined;
let str: string = u;
let vo: void= u;

// 編譯經過

而 void 類型的變量不能賦值給其餘類型的變量,只能賦值給 void 類型:

let u: void;
let num: number = u;  // Type 'void' is not assignable to type 'number'.
let vo: void = u;     // 編譯經過

任意值

  1. 任意值(Any)用來表示容許賦值爲任意類型。一個普通類型,在賦值過程當中是不被容許改變類型的,any 類型,容許被賦值爲任意類型。
let str: string = 'abc';
str = 123;
 
// Type 'number' is not assignable to type 'string'.
  
// -----------------------------------------------------------------
  
let str: any = 'abc';
str = 123;
  
// 編譯經過
  1. 任意值能夠訪問任意屬性和方法:
let anyThing: any = 'hello';
   
anyThing.setName('Jerry');
anyThing.setName('Jerry').sayHello();
anyThing.myName.setFirstName('Cat');
   
console.log(anyThing.myName);
console.log(anyThing.myName.firstName);
   
// 編譯經過
  1. 未聲明類型的變量:若是變量在聲明時,未指定類型,那麼會被識別爲任意值類型:
let something;
something = 'seven';
something = 7;

// 等價於
   
let something: any;
something = 'seven';
something = 7;

類型推論

若未明確指定類型,那麼 TS 會依照類型推論(Type Inference)規則推斷出一個類型。
如下代碼雖然沒有指定類型,但編譯時會報錯:

let data = 'seven';
data = 7;

// Type 'number' is not assignable to type 'string'.

事實上,它等價於:

let data: string = 'seven';
data = 7;

// Type 'number' is not assignable to type 'string'.

TS會在未明確指定類型時推測出一個類型,這就是類型推論。若是定義時未賦值,無論以後是否賦值,都會被推斷成 any 類型

let data;
data = 'seven';
data = 7;

// 編譯經過

聯合類型

聯合類型(Union Types)表示取值能夠爲多種類型中的一種,使用 | 分隔每一個類型。

let data: string | number;   // 容許 data 能夠是 string 或 number 類型,但不能是其餘類型
data = 'seven';
data = 7;

data = true;

//  Type 'boolean' is not assignable to type 'string | number'.

訪問聯合類型的屬性或方法:當不肯定一個聯合類型的變量究竟是哪一個類型時,只能訪問此聯合類型中全部類型共有的屬性或方法

function getLength(something: string | number): number {
    return something.length;
}

// length 不是 string 和 number 的共有屬性,因此會報錯
// Property 'length' does not exist on type 'string | number'.
// Property 'length' does not exist on type 'number'.

訪問 string 和 number 的共有屬性:

function getString(something: string | number): string {
    return something.toString();
}

聯合類型的變量在被賦值的時候,會根據類型推論的規則推斷出一個類型:

let data: string | number;
data = 'seven';
console.log(data.length); // 5
data = 7;
console.log(data.length); // 編譯時報錯

// Property 'length' does not exist on type 'number'.

line2:data 被推斷爲 string,訪問length 屬性不會報錯。

line4:data 被推斷爲 number,訪問length 屬性報錯。

對象的類型——接口

在 TS中,使用接口(Interfaces)來定義對象的類型。可用於對類的一部分行爲進行抽象之外,也經常使用於對對象的形狀(Shape)進行描述。

interface Person {
    name: string;
    age: number;
}

let p1: Person = {
    name: 'Tom',
    age: 25
};

定義一個接口 Person(接口通常首字母大寫),定義一個變量 tom 類型是 Person。這樣就約束了 tom 的形狀必須和接口 Person 一致。

肯定屬性

肯定屬性:賦值時,定義的變量的形狀必須與接口形狀保持一致。變量的屬性比接口少或多屬性都是不容許的:

interface Person {
    name: string;
    age: number;
}

let p1: Person = {  // 缺乏 age 屬性
    name: 'Tom'
};

// Type '{ name: string; }' is not assignable to type 'Person'.
// Property 'age' is missing in type '{ name: string; }'.


// -----------------------------------------------------------------


let p2 Person = {  // 多餘 gender 屬性
    name: 'Tom',
    age: 25,
    gender: 'male'
};

// Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.
// Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.

可選屬性

可選屬性:是指該屬性能夠不存在。有時不須要徹底匹配一個接口時,能夠用可選屬性,但此時仍然不容許添加未定義的屬性

interface Person {
    name: string;
    age?: number;
}

let p1: Person = {  // 編譯經過
    name: 'Tom'
};

let p2: Person = {  // 編譯經過
    name: 'Tom',
    age: 25
};

let p3: Person = {  // 報錯(同上)
    name: 'Tom',
    age: 25,
    gender: 'male'
};

任意屬性

任意屬性:容許一個接口有任意的屬性

interface Person {
    name: string;
    age?: number;
    [propName: string]: any;
}

let p1: Person = {
    name: 'Tom',
    gender: 'male'
};

使用 [propName: string] 定義了任意屬性的屬性名取 string 類型的值。屬性值爲任意值

注意:一旦定義了任意屬性,那麼肯定屬性和可選屬性的類型都必須是它的類型的子集:

例一:任意屬性的類型是 string,可是可選屬性 age 的值倒是 number,number 不是 string 的子屬性,因此會報錯。

interface Person {
    name: string;
    age?: number;  
    [propName: string]: string;
}

let p1: Person = {
    name: 'Tom',
    age: 25,
    gender: 'male'
};
 
// Property 'age' of type 'number' is not assignable to string index type 'string
// Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.
// Property 'age' is incompatible with index signature.
// Type 'number' is not assignable to type 'string'

例二:一個接口中只能定義一個任意屬性。若是接口中有多個類型的屬性,能夠在任意屬性中使用聯合類型

interface Person {
    name: string;
    age?: number;
    [propName: string]: string | number;
}

let p1: Person = { // 編譯經過
    name: 'Tom',
    age: 25,
    gender: 'male',
    year:2021
};

只讀屬性

對象中的一些字段只能在建立時被賦值,可使用 **readonly **定義只讀屬性

例一:使用 readonly 定義的屬性 id 初始化後,又被從新賦值,因此會報錯。

interface Person {
    readonly id: number;
    name: string;
    age?: number;
    [propName: string]: any;
}

let p1: Person = {
    id: 89757,
    name: 'Tom',
    gender: 'male'
};

p1.id = 9527;

// Cannot assign to 'id' because it is a read-only property.

例二:只讀的約束存在於第一次給對象賦值的時候,而不是第一次給只讀屬性賦值時:

interface Person {
    readonly id: number;
    name: string;
    age?: number;
    [propName: string]: any;
}

let p2: Person = {  // 第一次給對象賦值
    name: 'Tom',
    gender: 'male'
};

p2.id = 89757;

// Property 'id' is missing in type '{ name: string; gender: string; }' but required in type 'Person'  對 p2 賦值時,沒有給 id 賦值

// Cannot assign to 'id' because it is a read-only property.  id 是隻讀屬性

數組的類型

在 TS 中,有多種定義數組類型的方式。

類型 + 方括號 表示法

最簡單的方法是使用類型 + 方括號來表示數組:

let arr: number[] = [1, 1, 2];    // 數組元素中不容許出現其餘的類型

let arr1: number[] = [1, '1', 2]; // 報錯:Type 'string' is not assignable to type 'number'.

數組的一些方法的參數也會根據數組在定義時約定的類型進行限制:

let arr2: number[] = [1, 1, 2, 3, 5];

arr2.push('8'); 

//報錯:Argument of type '"8"' is not assignable to parameter of type 'number'.

數組泛型

使用數組泛型(Array Generic) Array<elemType> 來表示數組:

let arr3: Array<number> = [1, 1, 2, 3, 5];

泛型涉及內容較多,後期有時間會在整理一篇文章,敬請關注!

用接口表示數組

以前介紹了使用接口表示對象的類型,一樣接口也能夠用來描述數組:

interface NumberArray {
    [index: number]: number;
}
let arr: NumberArray = [1, 1, 2, 3, 5];

NumberArray 表示:索引的類型是數字,值的類型也是數字,這樣即可以表示一個數字類型的數組,雖然接口也能夠描述數組,可是通常不會這麼作,由於這種方式較複雜。有一例外,就是經常使用來表示類數組。

類數組

類數組(Array-like Object)不是數組類型,好比 arguments,其實是一個類數組,不能用普通數組的方式來描述,而應該用接口:

function sum() {
    let args: number[] = arguments;
}

// 報錯:Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.

接口描述類數組:除了約束索引的類型是數字,值的類型也必須是數字以外,也約束了它還有 length 和 callee 兩個屬性。

function sum() {
    let args: {
        [index: number]: number;
        length: number;
        callee: Function;
    } = arguments;
}

而事實上經常使用的類數組都有本身的接口定義,如 IArguments, NodeList, HTMLCollection 等:

function sum() {
    let args: IArguments = arguments;
}

//其中 IArguments 是 TypeScript 中定義好了的類型,它實際上就是:
interface IArguments {
    [index: number]: any;
    length: number;
    callee: Function;
}

any 在數組中的應用

一個比較常見的作法是,用 any 表示數組中容許出現任意類型:

let list: any[] = ['Echoyya', 25, { website: 'https://www.cnblogs.com/echoyya/' }, false];

後續筆記敬請期待~~~

相關文章
相關標籤/搜索