[TOC]
# 參考文檔
- [TypeScript 官方文檔](https://www.tslang.cn/docs/handbook/basic-types.html)
- [TypeScript 入門教程](https://ts.xcatliu.com/introduction/get-typescript)
# 安裝與配置(VSCode)
1)全局安裝:
```
npm install -g typescript
```
2)VSCode 配置:
3)編譯單個文件:運行`tsc filename.ts`即可對 .ts 文件進行編譯生成對應的 .js 文件。
# 原始數據類型
JavaScript 的數據類型分為兩種:原始數據類型(Primitive data types)和對象類型(Object types)。
原始數據類型包括:布爾值、數值、字符串、null、undefined、Symbol(ES6)。
```js
let isDone: boolean = false
let age: number = 20
let firstName: string = 'name'
let message: string = `Hello, ${firstName}` // 模板字符串
let u: undefined = undefined
let n: null = null
// undefined and null 是所有數據類型的子類型
// 例如 undefined 類型的變量可以賦值給 number 類型的變量
let num: number = undefined
// 空值:JavaScirpt 沒有空值(Void)的概念,在 TypeScript 中可以使用 void 表示沒有任何返回值的函數
function alertName(): void {
alert('My name is Tom')
}
```
# 任意值(Any)
Any 用來表示允許賦值為任意類型。
```js
let myFavoriteNumber: string = 'seven'
myFavoriteNumber = 7 // error TS2322: Type '7' is not assignable to type 'string'.
let myFavoriteNumber: any = 'seven'
myFavoriteNumber = 7 // √
```
在任意值上訪問任何屬性都是允許的,也可以調用任何方法。聲明一個變量為任意值后,對它的任何操作,返回的內容的類型都為任意值。
變量如果在聲明時未指定其類型,那么其會被識別為任意值類型。
# 類型推論(Type Inference)
如果沒有明確的指定類型,那么 TypeScript 會依照類型推論的規則推斷出一個類型。
如果定義的時候沒有賦值,不管之后有沒有賦值,變量都會被推斷成`any`類型而完全不被類型檢查。
```js
let myFavoriteNumber
myFavoriteNumber = 'seven'
myFavoriteNumber = 7
```
# 聯合類型(Union Types)
聯合類型表示取值可以為多種類型中的一種。聯合類型使用`|`分隔每個類型。
```js
let myFavoriteNumber: string | number
myFavoriteNumber = 'seven'
myFavoriteNumber = 7
```
當 TypeScript 不確定一個聯合類型的變量到底是哪個類型時。我們只能訪問此聯合類型的所有類型里共有的屬性或方法:
```
function getLength(something: string | number): number {
return something.length
}
// index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.
// Property 'length' does not exist on type 'number'.
```
`length`不是`string`和`number`的共有屬性,所以會報錯。
聯合類型的變量在被賦值的時候,會根據類型推論的規則推斷出一個類型:
```js
let myFavoriteNumber: string | number
myFavoriteNumber = 'seven'
console.log(myFavoriteNumber.length) // 5
myFavoriteNumber = 7
console.log(myFavoriteNumber.length) // 編譯時報錯
// index.ts(5,30): error TS2339: Property 'length' does not exist on type 'number'.
```
上例中,第二行的`myFavoriteNumber`被推斷成了`string`,訪問其`length`屬性不會報錯。
而第四行的`myFavoriteNumber`被推斷成了`number`,訪問其`length`屬性時就會報錯了。
# 對象的類型——接口
TypeScript 使用接口(Interfaces)來定義對象的類型。
在面向對象語言中,接口(Interfaces)是一個很重要的概念,它是對行為的抽象,而具體如何行動需要由類(classes)去實現(implement)。
TypeScript 中的接口是一個非常靈活的概念,除了可用于對類的一部分行為進行抽象以外,也常用于對對象的形狀(Shape)進行描述。
```
interface Person { // 接口一般首字母大寫
name: string;
age: number;
}
let tom: Person = {
name: 'Tom',
age: 25
}
```
上面的例子中,我們定義了一個接口`Person`,然后定義了一個變量`tom`,它的類型是`Person`。這樣,我們就約束了`tom`的形狀必須與接口`Person`一致。
定義的變量比接口少一些屬性或者多一些屬性都是不允許的,賦值的時候變量的形狀必須與接口的形狀保持一致。
## 可選屬性和任意屬性
有時我們希望不要完全匹配一個形狀,那么可以用可選屬性:
```js
interface Person {
name: string;
age?: number; // 該屬性可以不存在
}
let tom: Person = {
name: 'Tom'
}
```
有時我們希望一個接口允許有任意的屬性,可以使用如下方式:
```js
interface Person {
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
name: 'Tom',
gender: 'male'
}
```
使用`[propName: string]`定義了任意屬性取`string`類型的值。
需要注意的是,**一個接口只允許定義一個任意屬性,且一旦定義了任意屬性,那么確定屬性和可選屬性的類型都必須是它的類型的子集:**
```js
interface Person {
name: string;
age?: number;
[propName: string]: string; // wrong
[propName: string]: number; // wrong
[propName: string]: string | number; // right
}
```
## 只讀屬性
有時我們希望對象的一些字段只能在創建的時候被賦值,那么可以用`readonly`定義只讀屬性:
```js
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
id: 12345,
name: 'Tom',
gender: 'male'
}
tom.id = 9527 // Cannot assign to 'id' because it is a read-only property.
```
注意,**只讀的約束存在于第一次給對象賦值的時候,而不是第一次給只讀屬性賦值的時候。**
# 數組與元組
## Array
在 TypeScript 中,數組類型有多種定義方式,比較靈活。
1)[類型+方括號] 表示法:
```js
let fibonacci: number[] = [1, 1, 2, 3, 5]
```
數組中的項不允許出現其他類型,且數組的一些方法的參數也會根據數組在定義時約定的類型進行限制:
```js
let fibonacci: number[] = [1, '1', 2, 3, 5]
// Type 'string' is not assignable to type 'number'.
```
```js
let fibonacci: number[] = [1, 1, 2, 3, 5]
fibonacci.push('8')
// Argument of type '"8"' is not assignable to parameter of type 'number'.
```
2)數組泛型(Array Generic):使用`Array<elemType>`的形式來表示數組:
```js
let fibonacci: Array<number> = [1, 1, 2, 3, 5]
```
3)用接口表示數組:
```js
interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5]
```
`NumberArray`表示:只要索引的類型是數字時,那么值的類型必須是數字。
雖然接口也可以用來描述數組,但是我們一般不會這么做,因為這種方式比前兩種方式復雜多了。但是其常用于表示類數組。
## Tuple
TypeScript 中的元組可以理解為在一定程度上限制數據類型的數組。
當我們想在數組中放不同類型的數據時就可以使用 Tuple。
```js
let user: [string, number] = ['str', 1]
```
# 函數的類型
在 JavaScript 中,有兩種常見的定義函數的方式——函數聲明和函數表達式:
```js
// 函數聲明(Function Declaration)
function sum (x, y) {
return x + y
}
// 函數表達式(Function Expression)
let mySum = function (x, y) {
return x + y
};
```
在 TypeScript 中函數聲明的形式如下,需要對輸入和輸出進行約束:
```ts
function sum(x: number, y: number): number {
return x + y
}
```
定義一個函數表達式的形式如下:
```ts
let mySum = function (x: number, y: number): number {
return x + y
}
```
這樣是可以通過編譯的,但是上面的代碼只對等號右側的匿名函數進行了類型定義,而等號左邊的`mySum`是通過賦值操作進行類型推論得出來的。如果要手動給`mySum`添加類型,則應該這樣:
```ts
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y
}
```
注意這里的`=>`與 ES6 中的`=>`不同,在 TypeScript 的類型定義中,`=>`表示函數的定義,左邊是輸入類型,需要用括號括起來,右邊是輸出類型。
## 用接口定義函數的形狀
我們也可以使用接口的方式來定義一個函數需要符合的形狀:
```ts
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
```
采用函數表達式 | 接口定義函數的方式時,對等號左側進行類型限制,可以保證以后對函數名賦值時保證參數個數、參數類型、返回值類型不變。
## 可選參數
與接口的可選屬性類似,我們用`?`表示可選的參數:
```ts
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
```
需要注意的是,可選參數必須接在必需參數后面。換句話說,**可選參數后面不允許再出現必需參數了**。
## 參數默認值
在 ES6 中,我們允許給函數的參數添加默認值,**TypeScript 會將添加了默認值的參數識別為可選參數**:
```ts
function buildName(firstName: string, lastName: string = 'Cat') {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
```
此時就不受「可選參數必須接在必需參數后面」的限制了:
```ts
function buildName(firstName: string = 'Tom', lastName: string) {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');
```
## 剩余參數
ES6 中,可以使用`...rest`的方式獲取函數中的剩余參數(rest 參數):
```js
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
});
}
let a: any[] = [];
push(a, 1, 2, 3);
```
事實上,`items`是一個數組。所以我們可以用數組的類型來定義它:
```ts
function push(array: any[], ...items: any[]) {
items.forEach(function(item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);
```
注意,rest 參數只能是最后一個參數。
# 類型斷言(Type Assertion)
類型斷言可以用來手動指定一個值的類型。一般使用`值 as 類型`的語法。類型斷言的常見用途有以下幾種:
1)將一個聯合類型斷言為其中一個類型
當 TypeScript 不確定一個聯合類型的變量到底是哪個類型的時候,我們只能訪問此聯合類型的所有類型中共有的屬性和方法:
```ts
interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
function getName(animal: Cat | Fish) {
return animal.name;
}
```
而有時我們確實需要在不確定類型的時候就訪問其中一個類型特有的屬性或方法,比如:
找各種第三方庫的 @types 定義:
[http://microsoft.github.io/TypeSearch/](http://microsoft.github.io/TypeSearch/)
# 聲明文件
當使用第三方庫時,我們需要引用它的聲明文件,才能獲得對應的代碼補全、接口提示等功能。
常用的帶聲明文件的庫:
- axios 自帶,不需要額外安裝
# 類型別名與字符串字面量類型
## 類型別名
類型別名用來給一個類型起個新名字。
e.g:
```ts
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
```
上例中,我們使用`type`創建類型別名。
類型別名常用于聯合類型。如下所示:
```js
type NameResolver = () => string
type NameOrResolver = string | NameResolver
function getName (n: NameOrResolver): string { // 遇到函數參數為聯合類型時經常這么寫
if (typeof n === 'string') {
return n
} else {
return n()
}
}
```
## 字符串字面量類型
字符串字面量類型用來約束取值只能是某幾個字符串中的一個。
e.g:
```ts
type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
// do something
}
handleEvent(document.getElementById('hello'), 'scroll'); // 沒問題
handleEvent(document.getElementById('world'), 'dblclick'); // 報錯,event 不能為 'dblclick'
// index.ts(7,47): error TS2345: Argument of type '"dblclick"' is not assignable to parameter of type 'EventNames'.
```
上例中,我們使用`type`定了一個字符串字面量類型`EventNames`,它只能取三種字符串中的一種。
# 泛型
泛型(Generics)是指在定義函數、接口或類的時候,不預先指定具體的類型,而在使用的時候再指定類型的一種特性。
首先,我們來實現一個函數`createArray`,它可以創建一個指定長度的數組,同時將每一項都填充一個默認值:
~~~ts
function createArray(length: number, value: any): Array<any> {
let result = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
~~~
這里使用了數組泛型來定義返回值的類型。
這段代碼編譯不會報錯,但是一個顯而易見的缺陷是,它并沒有準確的定義返回值的類型:
`Array<any>`允許數組的每一項都為任意類型。但是我們預期的是,數組中每一項都應該是輸入的`value`的類型。
這時候,泛型就派上用場了:
~~~ts
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray<string>(3, 'x'); // ['x', 'x', 'x']
~~~
上例中,我們在函數名后添加了`<T>`,其中`T`用來指代任意輸入的類型,在后面的輸入`value: T`和輸出`Array<T>`中即可使用了。
接著在調用的時候,可以指定它具體的類型為`string`。當然,也可以不手動指定,而讓類型推論自動推算出來:
~~~ts
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
~~~
示例二:
```js
function swap<T, U> (tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]]
}
const result = swap(['string', 123])
```
## 泛型約束
在函數內部使用泛型變量的時候,由于事先不知道它是哪種類型,所以不能隨意的操作它的屬性或方法:
~~~ts
function loggingIdentity<T>(arg: T): T {
console.log(arg.length);
return arg;
}
// index.ts(2,19): error TS2339: Property 'length' does not exist on type 'T'.
~~~
上例中,泛型`T`不一定包含屬性`length`,所以編譯的時候報錯了。
這時,我們可以對泛型進行約束,只允許這個函數傳入那些包含`length`屬性的變量。這就是泛型約束:
~~~ts
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
~~~
上例中,我們使用了`extends`約束了泛型`T`必須符合接口`Lengthwise`的形狀,也就是必須包含`length`屬性。
此時如果調用`loggingIdentity`的時候,傳入的`arg`不包含`length`,那么在編譯階段就會報錯了:
~~~ts
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity(7);
// index.ts(10,17): error TS2345: Argument of type '7' is not assignable to parameter of type 'Lengthwise'.
~~~
多個類型參數之間也可以互相約束:
~~~ts
function copyFields<T extends U, U>(target: T, source: U): T {
for (let id in source) {
target[id] = (<T>source)[id];
}
return target;
}
let x = { a: 1, b: 2, c: 3, d: 4 };
copyFields(x, { b: 10, d: 20 });
~~~
上例中,我們使用了兩個類型參數,其中要求`T`繼承`U`,這樣就保證了`U`上不會出現`T`中不存在的字段。
## 泛型參數的默認類型
在 TypeScript 2.3 以后,我們可以為泛型中的類型參數指定默認類型。當使用泛型時沒有在代碼中直接指定類型參數,從實際值參數中也無法推測出時,這個默認類型就會起作用。
~~~ts
function createArray<T = string>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
~~~
## 泛型類
首先看如下代碼:假設我們創建了一個隊列類
```js
class Queue {
private data = []
push (item) {
return this.data.push(item)
}
pop () {
return this.data.shift()
}
}
const queue = new Queue()
queue.push(1)
queue.push('str')
console.log(queue.pop().tpFixed())
```
問題很明顯,我們可以往這個隊列中推入任何數據類型的數據,假設我們需要對 push 和 pop 的數據類型做限制,就可以使用泛型類。
```js
class Queue<T> {
private data = []
push (item: T) {
return this.data.push(item)
}
pop (): T {
return this.data.shift()
}
}
const queue = new Queue()
queue.push(1)
queue.push('str')
console.log(queue.pop().toFixed())
```
注:上面的代碼為偽代碼,TS 編輯器會報一些細節錯誤,最終修改如下:
- 序言 & 更新日志
- H5
- Canvas
- 序言
- Part1-直線、矩形、多邊形
- Part2-曲線圖形
- Part3-線條操作
- Part4-文本操作
- Part5-圖像操作
- Part6-變形操作
- Part7-像素操作
- Part8-漸變與陰影
- Part9-路徑與狀態
- Part10-物理動畫
- Part11-邊界檢測
- Part12-碰撞檢測
- Part13-用戶交互
- Part14-高級動畫
- CSS
- SCSS
- codePen
- 速查表
- 面試題
- 《CSS Secrets》
- SVG
- 移動端適配
- 濾鏡(filter)的使用
- JS
- 基礎概念
- 作用域、作用域鏈、閉包
- this
- 原型與繼承
- 數組、字符串、Map、Set方法整理
- 垃圾回收機制
- DOM
- BOM
- 事件循環
- 嚴格模式
- 正則表達式
- ES6部分
- 設計模式
- AJAX
- 模塊化
- 讀冴羽博客筆記
- 第一部分總結-深入JS系列
- 第二部分總結-專題系列
- 第三部分總結-ES6系列
- 網絡請求中的數據類型
- 事件
- 表單
- 函數式編程
- Tips
- JS-Coding
- Framework
- Vue
- 書寫規范
- 基礎
- vue-router & vuex
- 深入淺出 Vue
- 響應式原理及其他
- new Vue 發生了什么
- 組件化
- 編譯流程
- Vue Router
- Vuex
- 前端路由的簡單實現
- React
- 基礎
- 書寫規范
- Redux & react-router
- immutable.js
- CSS 管理
- React 16新特性-Fiber 與 Hook
- 《深入淺出React和Redux》筆記
- 前半部分
- 后半部分
- react-transition-group
- Vue 與 React 的對比
- 工程化與架構
- Hybird
- React Native
- 新手上路
- 內置組件
- 常用插件
- 問題記錄
- Echarts
- 基礎
- Electron
- 序言
- 配置 Electron 開發環境 & 基礎概念
- React + TypeScript 仿 Antd
- TypeScript 基礎
- React + ts
- 樣式設計
- 組件測試
- 圖標解決方案
- Storybook 的使用
- Input 組件
- 在線 mock server
- 打包與發布
- Algorithm
- 排序算法及常見問題
- 劍指 offer
- 動態規劃
- DataStruct
- 概述
- 樹
- 鏈表
- Network
- Performance
- Webpack
- PWA
- Browser
- Safety
- 微信小程序
- mpvue 課程實戰記錄
- 服務器
- 操作系統基礎知識
- Linux
- Nginx
- redis
- node.js
- 基礎及原生模塊
- express框架
- node.js操作數據庫
- 《深入淺出 node.js》筆記
- 前半部分
- 后半部分
- 數據庫
- SQL
- 面試題收集
- 智力題
- 面試題精選1
- 面試題精選2
- 問答篇
- 2025面試題收集
- Other
- markdown 書寫
- Git
- LaTex 常用命令
- Bugs