《TypeScript入門教程》筆記
基礎
原始資料型別
布林值
let isDone: boolean = false;
數值
let decLiteral: number = 6;
字串
let myName: string = 'tom';
null/undefined
1.示例
let myName: null = null;
let myName: undefined = undefined;
2.它們是所有型別的子型別
let val: 其它型別 = undefined;
非原始資料型別
空值
1.關鍵字為void,只能被賦值undefined和null
let unusable: void = undefined;
任意值
1.可以賦值為任意型別的值(沒有型別限制)
let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7;
2.對任意值進行任何操作,返回值型別都是任意值
3.變數宣告時,如果未指定型別
a.未賦值,則會預設為any型別 let something; something = 'seven'; something = 7; b.賦值了,則會被型別推斷為所賦值的型別 let myFavoriteNumber = 'seven'; myFavoriteNumber = 7; // 會報錯
聯合型別
1.表示取值可以為多種型別中的一種
2.示例
let myFavoriteNumber: string | number
myFavoriteNumber = 'seven'
myFavoriteNumber = 7
3.當不確定聯合型別變數的型別時,只能訪問此聯合型別的所有型別裡共有的屬性或方法
function getString(something: string | number): string {
return something.toString()
}
物件型別·介面
1.物件的型別由介面定義
2.介面是對行為的抽象,而具體實現則由類去實現
3.示例程式碼
interface Person {
name: string
age: number
}
let tom: Person = {
name: 'Tom',
age: 25
}
4.物件定義時,變數的屬性數量/形狀必須和介面屬性數量/形狀保持一致
5.可選屬性
interface Person {
name: string
age?: number
}
let tom: Person = {
name: 'Tom'
}
5.任意屬性(一旦定義了任意屬性,那麼確定屬性和可選屬性的型別都必須是它的子型別)
interface Person {
name: string
age: number
[propName: string]: any
}
let tom: Person = {
name: 'Tom',
age: 25 // 會報錯
}
6.只讀屬性
interface Person {
readonly id: number
age: number
}
let tom: Person = {
id: 10,
age: 25
}
tom.id = 89757 // 報錯
陣列型別
1.型別+方括號表示法
let arr: number[] = [1, 1, 2, 3, 5];
let arr: number[] = [1, 1, 2, 3, '5']; // 會報錯
arr.push('5'); // 會報錯
2.陣列泛型
let fibonacci: Array<number> = [1, 1, 2, 3, 5]
3.介面表示陣列
interface NumberArray {
[index: number]: number
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5]
4.any陣列
- 用any表示陣列中允許出現任意型別
- 示例
let list: any[] = ['xjh', 25, { key: 'xx' }]
5.類陣列
- a.常見的類陣列都有自己的介面定義,如 IArguments, NodeList, HTMLCollection
- b.示例
function sum() {
let args: IArguments = arguments
}
函式型別
1.函式宣告
- 示例
function sum(x: number, y: number): number {
return x + y;
}
- 輸入多餘/少於要求的引數,是不被允許的
sum(1, 2, 3) // 報錯
sum(1) // 報錯
2.函式表示式
- 示例
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y
}
- =>用來表示函式的定義,左邊是輸入型別,需要用括號括起來,右邊是輸出型別
3.用介面定義函式的形狀
interface SearchFunc {
(source: string, subString: string): boolean
}
let mySearch: SearchFunc = function(source: string, subString: string) {
return source.search(subString) !== -1
}
4.可選引數
- 可選引數,必須接在必需引數後面
- 可選引數,後面不允許再出現必須引數
5.引數預設值
- 同ES6
- 會將添加了預設值的引數,識別為可選引數
- 不受"可選引數必須接在必需引數後面"的限制
6.剩餘引數
- 同ES6
7.過載
- 允許一個函式接受不同數量或型別的引數時,作出不同的處理
- 示例
function reverse(x: number): number
function reverse(x: string): string
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''))
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
- 如果多個函式定義有包含關係,需要優先把精確的定義寫在前面
型別斷言
1.手動指定一個值的型別
2.方法
- <型別>值
- 值 as 型別
3.斷言成一個聯合型別中不存在的型別是不允許的
function toBoolean(something: string | number): boolean {
return <boolean>something
}
進階
類型別名
定義
- 用來給一個型別起個新名字
示例程式碼
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
字串字面量型別
定義
- 用來約束取值只能是某幾個字串中的一個
示例程式碼
type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {}
注意
- 類型別名與字串字面量型別都是使用type進行定義
元祖Tuple
定義
- 數組合並了相同型別的物件,而元組合並了不同型別的物件
示例
let tom: [string, number] = ['Tom', 25];
注意
1.當賦值或訪問一個已知索引的元素時,可以只賦值其中一項
let tom: [string, number];
tom[0] = 'Tom';
2.直接對元祖型別賦值時,需提供所有型別項
let tom: [string, number];
tom = ['Tom', 25];
3.當新增越界元素時,型別會被限制為定義項的型別
let tom: [string, number];
tom = ['Tom', 25];
tom.push('male');
tom.push(true); // 報錯
列舉
定義
- 用於取值被限定在一定範圍內的場景
簡單示例
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 0); // true
console.log(Days[0] === "Sun"); // true
手動賦值
enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 7); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Sat"] === 6); // true
說明
- 未手動賦值的列舉項會接著上一個列舉項遞增
- 未手動賦值的列舉項與手動賦值的重複了,ts不會察覺,後者只會覆蓋前者
列舉項型別
1.常數項和計算所得項
2.計算所得項·示例
enum Color { Red, Green, Blue = "blue".length };
3.如果緊接在計算所得項後面的是未手動賦值的項,那麼它就會因為無法獲得初始值而報錯
enum Color { Red = "red".length, Green, Blue }; // 報錯
常數列舉
1.示例程式碼
const enum Directions {
Up,
Down,
Left,
Right
}
let directions = [ Directions.Up, Directions.Down, Directions.Left, Directions.Right ];
var directions = [0, 1, 2, 3]; //編譯結果
2.常數列舉與普通列舉的區別是,它會在編譯階段被刪除,並且不能包含計算成員
外部列舉
1.示例程式碼
declare enum Directions {
Up,
Down,
Left,
Right
}
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]; //編譯結果
2.declare 定義的型別只會用於編譯時的檢查,編譯結果中會被刪除
3.外部列舉與宣告語句一樣,常出現在宣告檔案中
類
概言
- TS除了實現了所有ES6中的類的功能以外,還添加了一些新的用法
修飾符
public 可被公開訪問
private 僅供自身訪問,子類也不可以訪問它的屬性/方法
protected 受保護的訪問,子類可以訪問它的屬性/方法
抽象類
用abstract關鍵字定義(抽象類/抽象方法)
不允許被例項化
抽象方法必須被子類實現
類與介面
類實現介面
把各層級的類之間共有的特性提取出來進行實現的部分叫介面
介面通過interface定義,通過implements實現
一個類只能繼承一個類,但是可以實現多個介面
介面繼承介面
- 通過extends關鍵字來繼承
介面繼承類
- 通過extends關鍵字來繼承
泛型
定義
在定義函式、介面或類的時候,不預先指定具體的型別,而在使用的時候再指定型別的一種特性
- 示例程式碼
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']
多個型別引數
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
swap([7, 'seven']); // ['seven', 7]
泛型約束
1.由於事先不知道它是哪種型別,所以不能隨意的操作它的屬性或方法(會報錯)
function loggingIdentity<T>(arg: T): T {
console.log(arg.length);
return arg;
}
2.對泛型進行約束
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
3.泛型之間也可以相互約束
泛型介面
寫法一
interface CreateArrayFunc {
<T>(length: number, value: T): Array<T>;
}
寫法二
interface CreateArrayFunc<T> {
(length: number, value: T): Array<T>;
}
泛型類
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
泛型引數的預設型別
1.當使用泛型時沒有在程式碼中直接指定型別引數,從實際值引數中也無法推測出時,這個預設型別就會起作用
2.示例程式碼
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;
}
宣告檔案
宣告檔案
概要
1.使用第三方庫時,需要引用它的宣告檔案,才能獲得對應的程式碼補全、介面提示等功能
2.宣告語句中只能定義型別,切勿在宣告語句中定義具體的實現
3.示例程式碼
declare var jQuery: (selector: string) => any
4.注意
- 帶有宣告語句的檔案即為宣告檔案
- 宣告檔案必需以 .d.ts為字尾
- 如果ts無法解析宣告檔案,檢查tsconfig.json中的files、include和exclude配置,確保其包含了對應庫的xx.d.ts檔案
5.第三方宣告檔案
- 通常使用@types統一管理第三方庫的宣告檔案
- 示例
npm install @types/jquery --save-dev
宣告合併
1.如果定義了兩個相同名字的函式、介面或類,那麼它們會合併成一個型別
2.合併的屬性型別必須是唯一的
3.函式合併
- 參考函式過載示例
4.介面合併
interface Alarm {
price: number;
}
interface Alarm {
weight: number;
}
// 等同於
interface Alarm {
price: number;
weight: number;
}
5.類合併
- 與介面的合併規則一致
書寫宣告檔案
1.宣告合併
- 一個東西既可以是函式,也可以是物件(擁有子屬性)
- 示例
declare function jQuery(selector: string): any
declare namespace jQuery {
function ajax(url: string, settings?: any): void
}
2.export
- 宣告檔案中禁止定義具體的實現
- interface前是不需要declare的
3.export default
- 只有 function、class 和interface可以直接預設匯出,其他變數需先宣告,再預設匯出
- 一般會將匯出語句放在整個宣告檔案的最前面
4.commonjs規範下的export
- commonjs匯出
module.exports = foo; // 整體匯出
exports.bar = bar; // 單個匯出
- 在匯入方式上,有兩種跟es6語法相同
- 官推匯入方式(commonjs下)
import foo = require('foo'); // 整體匯入
import bar = foo.bar; // 單個匯入
- 官推匯出方式(commonjs下)(假如要為它寫型別宣告檔案的話)
export = foo
declare function foo(): string
- 兩種官推方式都是ts為了相容AMD和commonjs規範而創立的
5.UMD庫的匯出
export as namespace '匯出名'
6.在npm包/UMD庫中擴充套件全域性變數
declare global {
interface String {
prependHello(): string
}
}
7.模組外掛(declare module)
// index.d.ts宣告
import * as moment from 'moment';
declare module 'moment' {
export function foo(): moment.CalendarKey
}
// index.ts使用
import * as moment from 'moment';
import 'moment-plugin';
moment.foo();
8.宣告檔案中的依賴
- 示例參考<模組外掛index.d.ts宣告>
- 三斜槓指令
- 早期版本中為了描述模組之間的依賴關係而創造的語法
- 使用場景:書寫一個全域性變數,並且需要依賴一個全域性變數宣告檔案時
- 示例
/// <reference types="jquery" /> declare function foo(options: JQuery.AjaxSettings): string
- 三斜線指令必須放在檔案的最頂端,它前面只允許出現單行或多行註釋
- 拆分宣告檔案
- 示例
/// <reference types="sizzle" /> /// <reference path="JQueryStatic.d.ts" /> /// <reference path="JQuery.d.ts" /> /// <reference path="misc.d.ts" /> /// <reference path="legacy.d.ts" /> export = jQuery;
- types用於宣告對另一個庫的依賴,path用於宣告對另一個檔案的依賴
9.自動生成宣告檔案
- 命令列
- 在命令列中新增--declaration(簡寫-d)
- 配置檔案
- 在tsconfig.json中新增declaration選項
- 示例程式碼
{ "compilerOptions": { "module": "commonjs", "outDir": "lib", "declaration": true } }
釋出宣告檔案
1.將宣告檔案和原始碼放在一起(ts宣告查詢也遵循a=>b=>c)
- package.json中配置types或typings欄位,指定宣告檔案
- 在專案根目錄下,放置一個index.d.ts宣告檔案
- 針對package.json的入口檔案位置,放置一個宣告檔案
2.將宣告檔案釋出到@types下
- 與普通的npm模組不同,@types統一由DefinitelyTyped管理
內建物件的宣告檔案
TypeScript核心庫定義中,包含了所有遊覽器環境用到的型別(ECMAScript,DOM和BOM標準)
TypeScript核心庫定義中,不包含Nodejs部分(需要npm install @types/node --save-dev)