本文是我的學習TypeScript中的我的總結, 其中主要是一些重要經常使用的點, 還有一些不太好理解的問題, 同時也參考了一些網上文章, 但願能夠幫助初學者快速入門,若有不對之處, 還望指出,感謝~html
聯合類型
表示一個值能夠是幾種類型之一, 至關於集合交集. 豎線(|)分隔; 若一個值是聯合類型,只能訪問此聯合類型的全部類型裏共有的成員java
交叉類型
將多個類型合併爲一個類型, 至關於集合並集. 豎線(&)分隔;把現有的多種類型疊加到一塊兒成爲一種類型,它包含了所需的全部類型的特性,多用於mixinsnode
映射類型:從舊類型建立新類型的一種方式 (包含Readonly Partial Record Pick)react
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
type Partial<T> = {
[P in keyof T]?: T[P];
}
interface Person {
name: string;
age: number;
}
// 調用
type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
複製代碼
// 結果
type PersonPartial = {
name?: string|undefined;
age?: number|undefined;
}
type PersonReadonly {
readonly name: string;
readonly age: number;
}
複製代碼
用戶自定義類型保護:定義函數,返回類型謂詞(語法:parameterName is Type)
git
typeof類型保護:只有兩種形式能被識別typeof v === "typename"
和 typeof v !== "typename"
, "typename"必須是 "number", "string", "boolean"或 "symbol" 原始類型,若爲除前四個的其餘字符串,將不被識別爲類型保護es6
instanceof 類型保護:過構造函數來細化類型的一種方式。語法: 實例 instanceof 構造函數
typescript
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}
interface Person {
name: string;
age: number;
}
let person: Person = {
name: 'Jarid',
age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, ['Jarid]
複製代碼
keyof T
, 索引類型查詢操做符 (此實例中,爲person的屬性name、age,值等於'name'|'age')
K extends keyof T
,泛型約束
T[K]
, 索引訪問操做符(此實例中,爲person['name'])express
小結:(如下圖來自於網絡, 感謝~) 編程
泛型(Generics)是指在定義函數、接口或類的時候,不預先指定具體的類型,而在使用的時候再指定類型的一種特性。
類型變量(類型參數) 通常用T
表示json
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
複製代碼
使用含有泛型的接口來定義函數形狀
interface ConfigFn{
<T>(value:T):T;
}
var getData:ConfigFn=function<T>(value:T):T{
return value;
}
getData<string>('張三');
getData<string>(1243); //錯誤
複製代碼
// 把類型參數提到接口名上
// 寫法一:
interface ConfigFn<T>{
(value:T):T;
}
var getData:ConfigFn<string>=function<T>(value:T):T{
return value;
}
getData('20'); /*正確*/
// 寫法二:
interface ConfigFn<T>{
(value:T):T;
}
function getData<T>(value:T):T{
return value;
}
var myGetData:ConfigFn<string>=getData;
myGetData('20'); /*正確*/
myGetData(20) //錯誤
複製代碼
使用泛型來定義類
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
複製代碼
一、抽象類是提供其餘類繼承的基類,不能直接被實例化.
二、用abstract關鍵字定義抽象類和抽象方法,抽象類中的抽象方法不包含具體實現而且必須在派生類中實現.
三、abstract抽象方法只能放在抽象類裏面.
四、可使用修飾符.
五、abstract 修飾,裏面能夠沒有抽象方法。但有抽象方法(abstract method)的類必須聲明爲抽象類(abstract class)
複製代碼
注意
:
1.使用多態基礎是類的繼承或者接口實現。
2.若是子類繼承的是一個抽象類,子類必須實現父類裏的抽象方法,否則的話不能實例化,會報錯。
3.多態的子類繼承父類,子類能夠不實現該方法,但使用多態就是子類爲了實現方法
複製代碼
class Greeter {
static standardGreeting = "Hello, there"; // 靜態屬性
greeting: string;
greet() {
if (this.greeting) {
return "Hello, " + this.greeting;
} else {
return Greeter.standardGreeting;
}
}
}
// typeof Greeter: 構造函數的類型, 包含類的靜態成員和構造函數
let greeterMaker: typeof Greeter = Greeter;
greeterMaker.standardGreeting = "Hey there!";
// Greeter: 實例類型
let greeter2: Greeter = new greeterMaker();
console.log(greeter2.greet());
複製代碼
在面向對象的編程中,接口是一種規範的定義,它定義了行爲和動做的規範; 接口定義了某一批類所須要遵照的規範,不關心這些類的內部狀態數據和其中方法的實現細節,它只規定這批類裏必須提供某些方法,提供這些方法的類就能夠知足實際須要.
interface Person {
readonly id: number; // 只讀屬性
name: string; // 肯定屬性
age?: number; // 可選屬性
[propName: string]: string; // 字符串索引
[index: number]: string; // 數字索引,相似數組
(name: string, age: number): void; // 函數
getName(id: number): string; // 方法
new(name: string, age: number): Person; // 構造函數
}
複製代碼
注
:
1.上面只是展現接口支持的功能, 不表明能夠所有寫在一塊兒.
2.若數字索引和字符串索引同時存在, 數字索引的返回值必須是字符串索引返回值類型的子類型. 由於當使用 number來索引時,JavaScript會將它轉換成string而後再去索引對象
3.字符串索引與其餘屬性同時存在, 其餘屬性值類型必須是字符串索引返回值類型的子類型. 由於字符串索引聲明瞭 obj.property和obj[「property」]兩種形式均可以
類實現接口, 用接口來明確的強制一個類去符合某種契約
// 接口描述類的實例部分
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) { } // 類靜態部分
}
複製代碼
類分爲靜態和實例部分:
當你用構造器簽名去定義一個接口並試圖定義一個類去實現這個接口時會獲得一個錯誤, 由於當一個類實現了一個接口時,只對其實例部分進行類型檢查。 constructor存在於類的靜態部分,因此不在檢查的範圍內
在實現接口時, 若想對構造函數和實例部分作約束, 能夠分別約束, 並經過一個新的構造函數(以下:createClock)生成實例
// ClockConstructor 爲構造函數所用
// ClockInterface 爲實例方法所用
// createClock的第一個參數是ClockConstructor類型,在createClock(AnalogClock, 7, 32)裏,會檢查AnalogClock是否符合構造函數簽名。
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick();
}
// 第一個參數ctor的類型是接口 ClockConstructor,在這裏就爲類的靜態部分指定須要實現的接口
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
return new ctor(hour, minute);
}
// 類 DigitalClock 實例化出來的對象(類的實例部分)應該知足這個接口的規則
class DigitalClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("beep beep");
}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("tick tock");
}
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
複製代碼
// 函數接口
interface Counter {
(start: number): string
}
let counter: Counter
counter = function (start: number) {
console.log(number)
}
// 調用
counter(12)
複製代碼
// 對象接口
interface Counter {
interval: number;
reset(): void;
}
複製代碼
下面的官方例子, 混合類型等同於上面兩個接口聲明合併
// 一個對象能夠同時作爲函數和對象使用,並帶有額外的屬性。如:下文中的變量c
interface Counter {
(start: number): string; // 函數
interval: number; // 對象屬性
reset(): void; // 對象方法
}
function getCounter(): Counter {
// 經過類型斷言,將函數對象轉換爲Counter類型,轉換後的對象不但實現了函數接口的描述,使之成爲一個函數,還具備interval屬性和reset()方法
let counter = <Counter>function (start: number) { console.log(number) };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
複製代碼
混合類型主要運用於相似UMD庫, 如老朋友jQuery, 既能夠做爲函數,也能夠做爲對象使用, 最近看Vue3的源碼, 發現裏面也存在, 以下:
export interface ReactiveEffect<T = any> {
(): T
_isEffect: true
active: boolean
raw: () => T
deps: Array<Dep>
options: ReactiveEffectOptions
}
function createReactiveEffect<T = any>(
fn: () => T,
options: ReactiveEffectOptions
): ReactiveEffect<T> {
const effect = function reactiveEffect(...args: unknown[]): unknown {
return run(effect, fn, args)
} as ReactiveEffect
effect._isEffect = true
effect.active = true
effect.raw = fn
effect.deps = []
effect.options = options
return effect
}
複製代碼
ps
: 此處主要講下,接口繼承類, 其餘你們應該理解, 或可自行查閱文檔
接口繼承類時,會繼承類的全部成員但不包含實現
class Person {
type: string // ️這裏是類的描述
}
interface Child extends Person { // ️Child 接口繼承自 Person 類,所以規範了 type 屬性
log(): void
// 這裏其實有一個 type: string
}
// 上面的 Child 接口繼承了 Person 對 type 的描述,還定義了 Child 接口自己 log 的描述
// 第一種寫法
class Girl implements Child {
type: 'child' // 接口繼承自 Person 的
log() {} // 接口自己規範的
}
// 第二種寫法
class Boy extends Person implements Child { // 首先 extends 了 Person 類,而後還需知足 Child 接口的描述
type: 'child'
log() {}
}
複製代碼
當接口繼承了一個擁有私有(private)或受保護(protected)的成員的類時,這個接口類型只能被這個類或其子類所實現(implement)。
class Person {
private type: string // ️這裏是類的描述
}
interface Child extends Person { // ️Child 接口繼承自 Person 類,所以規範了 type 屬性
log(): void
// 這裏其實有一個 type: string
}
// 上面的 Child 接口繼承了 Person 對 type 的描述,還定義了 Child 接口自己 log 的描述
// 寫法 (only)
class Boy extends Person implements Child { // 首先 extends 了 Person 類,而後還需知足 Child 接口的描述
type: 'child'
log() {}
}
複製代碼
總的來講,這種模式是應當避免的(不推薦),尤爲是在類擁有私有成員時.
一、函數聲明
function sum(x: number, y: number): number {
return x + y;
}
複製代碼
二、函數表達式
let mySum = function (x: number, y: number): number {
return x + y;
};
// 或者
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
複製代碼
注意:
1.可選參數(?:): 必須接在必需參數後面
2.參數默認值: 至關於可選參數,任意位置
3.剩餘參數(...rest): 必須是在最後,與 es6 相同
java中的重載:同名函數,參數不同. 容許一個函數接受不一樣數量或類型的參數時,做出不一樣的處理. typescript中的重載:經過爲同一個函數提供多個函數類型定義,一個函數體實現多種功能的目的. ts爲了兼容es5 以及 es6 重載的寫法和java中有區別.
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.此外, 重載的順序應該是匹配範圍從小到大, 由於重載匹配是從上到下, 匹配後再也不往下查找
複製代碼
指定編譯選項和項目待編譯文件, 主要包含如下選項:
TS 文件指拓展名爲 .ts、.tsx 或 .d.ts 的文件。若是開啓了 compilerOptions.allowJs 選項,那 .js 和 .jsx 文件也屬於 TS 文件
1. 若files 和 include 都未設置,那麼除了 exclude 排除的文件,編譯器會默認包含路徑下的全部 TS 文件。
2. 若同時設置 files 和 include ,那麼編譯器會把二者指定的文件都引入
3. 若未設置 exclude ,那其默認值爲 node_modules 、bower_components、jspm_packages 和編譯選項 outDir 指定的路徑
4. exclude 只對 include 有效,對 files 無效
5. {
"compilerOptions": {
"typeRoots" : ["./typings"], // typeRoots指定的目錄下的包, 會被編譯, 即指定被編譯包所在目錄
"types": ["node", "lodash", "express"] // 指定被編譯的包
}
}
複製代碼
--target: 指定編譯後的js版本
--lib: 編譯中包含的庫
// 其餘參考官方文檔
複製代碼
須要注意的點
:
1.不帶任何輸入文件的狀況下調用tsc,編譯器會從當前目錄開始去查找tsconfig.json文件,逐級向上搜索父目錄。
2.不帶任何輸入文件的狀況下調用tsc,且使用命令行參數--project(或-p)指定一個包含tsconfig.json文件的目錄。
3.當命令行上指定了輸入文件時,tsconfig.json文件會被忽略, 按照默認選項編譯該文件
複製代碼
定義類型有兩種方式: 接口(interface) 和類型別名(type alias)
一、interface 只能定義對象類型或者函數, type 還能夠定義組合類型,交叉類型(&,相似並集),聯合類型(|,相似交集),原始類型
二、interface 方式能夠實現接口的 extends 和 implements , 而 type alias 則不行。
三、interface 能夠實現接口的合併,但 type alias 則不行。
複製代碼
TypeScript 3.0 引入了新的unknown 類型,它是 any 類型對應的安全類型。
unknown: 在對 unknown 類型的值執行大多數操做以前,必須進行某種形式的檢查。
any: 在對 any 類型的值執行操做以前,沒必要進行任何檢查。
複製代碼
關於unknown可參考 juejin.im/post/5d04ac…
二者定義或修飾的值, 不能更改, 只讀
const: 用來定義(變)常量
readonly: 用來修飾對象屬性
複製代碼
我以爲學習一門語言,在熟悉文檔概念後, 最終應該在實踐加深體會,多踩踩坑,才能更好的掌握,畢竟學習的目的就是爲了更好的爲工做業務服務, 如今TS已經比較成熟了,你們確定都比較熟,若還未上車,能夠開始了.
以後有時間在總結下本身開發中遇到的問題, 感謝~~~ 關於文中很差不妥之處,還請多多包含並指出,第一次在掘金寫,手動捂臉!
www.typescriptlang.org/ www.tslang.cn/ ts.xcatliu.com/advanced/cl…
www.softwhy.com/article-860…
blog.poetries.top/2019/09/03/…
kbscript.com/2017/01/27/…
juejin.im/post/5c2723…