轉(zhuǎn)發(fā)
TypeScript基礎(chǔ)入門(mén)之高級(jí)類(lèi)型的交叉類(lèi)型和聯(lián)合類(lèi)型
項(xiàng)目實(shí)踐倉(cāng)庫(kù)
https://github.com/durban89/typescript_demo.git
tag: 1.4.2
為了保證后面的學(xué)習(xí)演示需要安裝下ts-node,這樣后面的每個(gè)操作都能直接運(yùn)行看到輸出的結(jié)果唾琼。
npm install -D ts-node
后面自己在練習(xí)的時(shí)候可以這樣使用
npx ts-node 腳本路徑
高級(jí)類(lèi)型
交叉類(lèi)型(Intersection Types)
交叉類(lèi)型是將多個(gè)類(lèi)型合并為一個(gè)類(lèi)型锡溯。 這讓我們可以把現(xiàn)有的多種類(lèi)型疊加到一起成為一種類(lèi)型,它包含了所需的所有類(lèi)型的特性芜茵。 例如甜癞, Person & Serializable & Loggable同時(shí)是 Person 和 Serializable 和 Loggable宛乃。 就是說(shuō)這個(gè)類(lèi)型的對(duì)象同時(shí)擁有了這三種類(lèi)型的成員征炼。您將主要看到用于mixins的交集類(lèi)型和其他不適合經(jīng)典面向?qū)ο竽>叩母拍睢#ㄔ贘avaScript中有很多這些Q刍怠)這是一個(gè)簡(jiǎn)單的例子酸些,展示了如何創(chuàng)建mixin:
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{}
for (let id in first) {
(<any>result)[id] = (<any>first)[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = (<any>second)[id];
}
}
return result
}
class AdvancedTypesClass {
constructor(public name: string){}
}
interface LoggerInterface {
log(): void;
}
class AdvancedTypesLoggerClass implements LoggerInterface {
log(): void {
console.log('console logging');
}
}
var logger = new AdvancedTypesLoggerClass();
var extend1 = extend(new AdvancedTypesClass("string"), new AdvancedTypesLoggerClass());
var e = extend1.name;
console.log(e);
extend1.log();
編譯運(yùn)行魄懂,注意這里要編譯運(yùn)行沿侈,我使用ts-node已經(jīng)不能運(yùn)行成功了缀拭√蠲保可能是哪里配置的有問(wèn)題,具體步驟如下褐荷。
tsc ./src/advanced_types_1.ts
$ node ./src/advanced_types_1.js
string
console logging
聯(lián)合類(lèi)型(Union Types)
聯(lián)合類(lèi)型與交叉類(lèi)型很有關(guān)聯(lián)嘹悼,但是使用上卻完全不同葵第。 偶爾你會(huì)遇到這種情況合溺,一個(gè)代碼庫(kù)希望傳入 number或 string類(lèi)型的參數(shù)。 例如下面的函數(shù):
/**
* 為給定的字符串左側(cè)添加"padding"
* 如果"padding"是一個(gè)字符串哮奇,則添加將字符串添加到給定字符串的左側(cè)
* 如果"padding"是一個(gè)數(shù)字睛约,則添加padding個(gè)數(shù)量的空格到給定字符串的左側(cè)
*/
function padLeft(value: string, padding: any) {
if (typeof padding === 'string') {
return padding + value;
}
if (typeof padding === 'number') {
return Array(padding + 1).join(' ') + value;
}
throw new Error(`Excepted string or number, got ${padding}`);
}
console.log("|" + padLeft("string", 4) + "|");
console.log("|" + padLeft("string", "a") + "|");
編譯并運(yùn)行后得到如下結(jié)果
$ tsc ./src/advanced_types_1.ts && node ./src/advanced_types_1.js
| string|
|astring|
padLeft有一個(gè)問(wèn)題,就是padding這個(gè)參數(shù)是一個(gè)any類(lèi)型贸伐,那就意味著我們可以在傳遞參數(shù)的時(shí)候怔揩,參數(shù)的類(lèi)型可以是number或者是string商膊,而TypeScript將會(huì)正常解析,
如果如下的方式調(diào)用藐翎,編譯的時(shí)候是可以正常解析的实幕,但是運(yùn)行的時(shí)候回報(bào)錯(cuò)
padLeft("Hello world", true);
在傳統(tǒng)的面向?qū)ο笳Z(yǔ)言里吝镣,我們可能會(huì)將這兩種類(lèi)型抽象成有層級(jí)的類(lèi)型。 這么做顯然是非常清晰的昆庇,但同時(shí)也存在了過(guò)度設(shè)計(jì)末贾。 padLeft原始版本的好處之一是允許我們傳入原始類(lèi)型。 這樣做的話使用起來(lái)既簡(jiǎn)單又方便凰锡。 如果我們就是想使用已經(jīng)存在的函數(shù)的話未舟,這種新的方式就不適用了。除了any掂为, 我們可以使用"聯(lián)合類(lèi)型"做為padding的參數(shù)裕膀,如下:
/**
* 為給定的字符串左側(cè)添加"padding"
* 如果"padding"是一個(gè)字符串,則添加將字符串添加到給定字符串的左側(cè)
* 如果"padding"是一個(gè)數(shù)字勇哗,則添加padding個(gè)數(shù)量的空格到給定字符串的左側(cè)
*/
function padLeft(value: string, padding: string | number) {
if (typeof padding === 'string') {
return padding + value;
}
if (typeof padding === 'number') {
return Array(padding + 1).join(' ') + value;
}
throw new Error(`Excepted string or number, got ${padding}`);
}
console.log("|" + padLeft("string", 4) + "|");
console.log("|" + padLeft("string", "a") + "|");
console.log("|" + padLeft("string", true) + "|");
編譯并運(yùn)行后得到如下結(jié)果
$ tsc ./src/advanced_types_1.ts && node ./src/advanced_types_1.js
src/advanced_types_1.ts:65:37 - error TS2345: Argument of type 'true' is not assignable to parameter of type 'string | number'.
65 console.log("|" + padLeft("string", true) + "|");
從實(shí)例演示可以看出,當(dāng)傳入一個(gè)boolean類(lèi)型值的時(shí)候,在編輯的時(shí)候TypeScript就做出了判斷抄谐,表示boolean類(lèi)型的參數(shù)不被支持
聯(lián)合類(lèi)型表示一個(gè)值可以是幾種類(lèi)型之一渺鹦。 我們用豎線(|)分隔每個(gè)類(lèi)型,所以 number | string | boolean表示一個(gè)值可以是 number蛹含, string毅厚,或 boolean。
如果一個(gè)值是聯(lián)合類(lèi)型浦箱,我們只能訪問(wèn)此聯(lián)合類(lèi)型的所有類(lèi)型里共有的成員吸耿,如下實(shí)例
interface Type1 {
func1(): void;
func2(): void;
}
interface Type2 {
func3(): void;
func2(): void;
}
class Type1Class implements Type1 {
func1(): void {
console.log('func1 run');
}
func2(): void {
console.log('func2 run');
}
}
class Type2Class implements Type2 {
func3(): void {
console.log('func1 run');
}
func2(): void {
console.log('func2 run');
}
}
function getSomeType(type: string): Type1Class | Type2Class {
if (type === '1') {
return new Type1Class();
}
if (type === '2') {
return new Type2Class();
}
throw new Error(`Excepted Type1Class or Type2Class, got ${type}`);
}
let type = getSomeType('1');
type.func2();
type.func1(); // 報(bào)錯(cuò)
編譯并運(yùn)行后得到如下結(jié)果
$ tsc ./src/advanced_types_1.ts
src/advanced_types_1.ts:111:6 - error TS2551: Property 'func1' does not exist on type 'Type1Class | Type2Class'. Did you mean 'func2'?
Property 'func1' does not exist on type 'Type2Class'.
111 type.func1();
這里的聯(lián)合類(lèi)型可能有點(diǎn)復(fù)雜,但是你很容易就習(xí)慣了酷窥。 如果一個(gè)值的類(lèi)型是 A | B咽安,我們能夠 確定的是它包含了 A 和 B中共有的成員。 這個(gè)例子里蓬推, Type1Class具有一個(gè)func1成員妆棒。 我們不能確定一個(gè) Type1Class | Type2Class類(lèi)型的變量是否有func1方法。 如果變量在運(yùn)行時(shí)是Type1Class類(lèi)型沸伏,那么調(diào)用type.func1()就出錯(cuò)了糕珊。
本實(shí)例結(jié)束實(shí)踐項(xiàng)目地址
https://github.com/durban89/typescript_demo.git
tag: 1.4.3