基礎
布爾值
let isDone: boolean = false;
數值
let decLiteral: number = 6;
字符串
let myName: string = 'tom';
無值
1.示例
let myName: null = null;
let myName: undefined = undefined;
2.null/undefined是所有類型的子類型
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: number
age?: number
[propName: string]: string
}
// 報錯 - 確定屬性和可選屬性的返回值類型必須和它的返回值類型匹配,或者是它的返回值類型的子集
class Animal {
name: string;
}
class Dog extends Animal {
breed: string;
}
// 錯誤 - 可索引的屬性可以多個,數字索引的返回值必須是字符串索引返回值類型的子類型
interface NotOkay {
[x: number]: Animal;
[x: string]: Dog;
}
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, y) {
return x + y
}
- =>用來表示函數的定義,左邊是輸入類型,需要用括號括起來,右邊是輸出類型
3.用接口定義函數的形狀
interface SearchFunc {
(source: string, subString: string): boolean
}
let mySearch: SearchFunc = function(source, subString) {
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('');
}
}
- 如果多個函數定義有包含關系,需要優先把精確的定義寫在前面
進階
類型推斷
定義
- 如果沒有明確指定類型,那么ts會依照類型推論規則推斷出一個類型
代碼示例
let myFavoriteNumber = 'seven';
myFavoriteNumber = 7; // 報錯,因為已推斷為字符串
強調
- 如果定義的時候沒有賦值,會被推斷成any類型
let myFavoriteNumber;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
類型斷言
1.手動指定一個值的類型,然后才可以使用類型對應的方法,否則只能訪問公共屬性/方法
function getLength(something: string | number): number {
if ((<string>something).length) {
return (<string>something).length;
} else {
return something.toString().length;
}
}
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.常數枚舉與普通枚舉的區別是,它會在編譯階段被刪除,並且不能包含計算所得項
const enum Color {Red, Green, Blue = "blue".length}; // 報錯
外部枚舉
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]
泛型默認類型
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.由於事先不知道它是哪種類型,所以不能隨意的操作它的屬性或方法
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.泛型之間也可以相互約束
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 });
4.在泛型約束中使用類型參數
function getProperty<T, K extends keyof T> (obj: T, key: K ) {
return obj[key]
}
let x = {a: 1, b: 2, c: 3, d: 4}
getProperty(x, 'a') // okay
getProperty(x, 'm') // error
泛型接口
寫法一
interface A {
<T>(arg: T): T;
}
function fn<T>(arg: T): T {
return arg;
}
let test: A = fn;
test<number>(1); // 手動指定-在調用時 傳遞 泛型參數類型
test(1); // 類型推斷-在調用時 推斷 泛型參數類型
寫法二
interface A<T> {
(arg: T): T;
}
function fn<T>(arg: T): T {
return arg;
}
let test: A<number> = fn; // 接口定義了需要傳遞類型參數T,這里為number
test<number>(1) // 報錯,因為類型定義A<number>已經指定
test('1'); // 報錯,必須符合接口A<number>參數類型
泛型類
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
聲明文件
概要
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)