總目錄
C# vs TypeScript - 高級類型
上一篇講了基礎類型,基本上用基礎類型足夠開發了,不過如果要更高效的開發,還是要看下高級類型,這篇和C#共同點並不多,只是延用這個主題。
聯合類型
可以從字面上進行理解:其實就是多個類型聯合在一起,用|
符號隔開。
如: string | number
, 表示希望這個類型既可以是string
,又可以是number
。
聯合類型的字段只能調用這些類型共同擁有的方法,除非類型推論系統自動判斷出真正的類型。
//這里sn就是一個聯合類型的字段,由於類型推論推斷出sn肯定是string,所以sn可以調用string的所有方法
let sn: string | number = 'string, number';
//這里就推斷不出具體的類型,只能調用toString, toValue了
function snFunc(): string | number{
return 'string, number';
}
聯合類型不光是可以聯合基本類型,也可以是用戶自定義的class, interace
等。
交叉類型
有|
就有&
,交叉類型就是用&
符號隔開,表示把多個類型合在一起,新類型包含所有類型的功能。
一些語言如Python有mixins
功能,用這個就很容易做到,主要是類似多重繼承,不過個人不是用喜歡這個,明顯違反了單一原則。
下面這段代碼就顯示了mixins
結合兩個類的功能,看起來是不是有點不大合理,目前的趨勢也是組合優先,用組合同樣也可以做到這些。
class Person {
talk(): string {
return `一個人`;
}
}
class Dog {
bark(): string {
return '汪汪汪';
}
}
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{};
for (let func of Object.getOwnPropertyNames(Object.getPrototypeOf(first))) {
(<any>result)[func] = (<any>first)[func];
}
for (let func of Object.getOwnPropertyNames(Object.getPrototypeOf(second))) {
(<any>result)[func] = (<any>second)[func];
}
return result;
}
let personDog = extend(new Person(), new Dog());
console.info(personDog.talk());
console.info(personDog.bark());
類型轉換
C#里常用的類型轉換一個是前面圓括號加類型,一個是as
。
TypeScript和C#一樣,只不是圓括號改成尖括號。
let test: any = '123';
let str1: string = <string>test;
let str2: string = test as string;
類型保護
聯合類型返回的是多個類型的其中一個,但是用的時候並不知道是哪個,需要一個一個判斷,這顯得很麻煩。
function get(): number | string{
return 'test';
}
let test = get();
var len = test.length; //編譯不了,不知道test到底是number還是string
let str = '';
if((<string>test).sub){
// string
} else {
// number
}
除了通過是否有string特有的方法來判斷是否是string,也可以用類似C#的typeof
來得到它的類型,而且重要的是會提供類型保護機制,
即在typeof
作用域里會知道這個變量的類型。
function get(): number | string{
return 'test';
}
let test = get();
if(typeof test === 'string'){
console.info(test.length); // 這里由於typeof確定了test類型是string,所以作用域內可以直接取length,而不用<string>轉一次
}
typeof
比較是有限制的,自己創建的類返回的都是object
,這時會用到instanceof
,並且instanceof
同樣會提供類型保護機制。
另外還有類型斷言可以提供類似的功能,不過不如上面的來得方便。
function get(): number | string{
return 'test';
}
let test = get();
function isStr(p : number | string): p is string{
return (<string>p).sub !== 'undefined';
}
if(isStr(test)) {
console.info(test.length);
} else {
console.info(test + 1);
}
上面p is string
就是斷言參數p
是string
類型,從而在isStr
后可以自動得到test
的類型,並且在else
里也知道是number
類型。
這點上比C#來得好,一般C#做法可能是用as
操作符轉過來,然后判斷是否為空,如果類型多操作起來也很復雜。
類型別名
類型別名即可以為現有類型取一個新名字。
type newstring = string;
let str: newstring = 'aaa';
console.info(str.length);
在C#中也可以用using strList = System.Generic.List
做個別名,不過還是不一樣,C#的是可以實例化的。
TypeScript別名不是新建一個類型,而是現有類型的一個引用。
給現在類型起別名意義不大,倒是可以配合聯合類型或交叉類型做成一些可讀的或比較新穎的類型。
別名也支持泛型,現在就有一個用別名創建了一個Tree類型,不過也只是別名,不能實例化,只能是看的,這點不如接口實在。
class Chicken{}
class Duck{}
type Fowl = Chicken | Duck;
type Tree<T> = {
value: T;
left: Tree<T>;
right: Tree<T>;
}
字符串字面量類型
TypeScript可以讓string成為一個類型,比如let strType = 'string type'
。
這個可以用在方法參數中,用來限制參數的輸入。
function test(param1: 'test1' | 'test2' | 'test3'){
}
test('test'); // 編譯不了,參數只能是test1, test2或test3
可辨識聯合
綜合上面的字符串字面量類型、聯合類型、類型保護、類型別名可以創建一個可辨識聯合的模式。
必須要在自定義的多個類中有相同的字段,這個字段用的是字符串字面量類型並且把這些類型聯合起來。
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
// 這里就可以用可辨識聯合
function area(s: Shape) {
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.height * s.width;
case "circle": return Math.PI * s.radius ** 2;
}
}
類型推論
TypeScript可以根據賦值或上下文推論出變量的類型,所以有時可以不用明確標明變量或函數返回值的類型。
let x = 123; // 這里能推論出x是number,就不用寫成這樣: let x: number = 123;
function get(){
return [1, 2, 3];
}
let arr = get(); // 這里也能推論出arr是number[];
function get(){
return [1, '2', 3];
}
let arr = get(); // 這里能推論出arr是(number | string)[];
不過個人覺得除了一些很明顯的let x = 123
之類可以不寫,其他的最好還是寫上類型,增加代碼可讀性。
以上就是TypeScript的類型了,比較靈活也比較難,可能要在實際項目中用用就會比較好掌握。