2.TypeScript 基礎入門(二)

變量類型的那些事

1.基本註解

類型註解使用:TypeAnnotation 語法。類型聲明空間中可用的任何內容均可以用做類型註解。javascript

const num: number = 123;
function identity(num:number):number{
    return num;
}

複製代碼

加入註解之後會報錯的寫法:java

const num: number = 123;
function identity(num: number): number {
  const num1 = '123'
  // 返回的不是 number  報錯
  return num1;
}
const num1 = '123' 
// 參數不是 number 報錯
identity(num1)
複製代碼

2. 原始類型

Javascript原始類型也一樣適用於 TypeScript的類型系統。所以,string, number,boolean也能夠被用做類型註解:算法

let num: number;
 let str: string;
 let bool: boolean;

 num = 123;
 num = 123.45;
 num = '123'; //Type '"123"' is not assignable to type 'number'

 str = '123';
 str = 123; //Type '123' is not assignable to type 'string'

 bool = true;
 bool = false;
 bool = 'false';//Type '"false"' is not assignable to type 'boolean'.
複製代碼

3. 數組

TypeScript 爲數組提供了專用的類型語法,所以你能夠很輕易的註解數組。它使用後綴[],接着你能夠根據須要補充任何有效的類型註解(如:boolean[])。它能讓你安全的使用任何有關數組的操做,並且它能放置一些相似賦值錯誤類型給成員的行爲。數組

你有兩種定義數組的方式:安全

第一種,你能夠在元素類型後面接上[],表示由此類型元素組成的一個數組:bash

let list:number[] = [1,2,3]
複製代碼

第二種方式是使用數組泛型,Array<元素類型>:數據結構

let list: Array<number> = [1,2,3]
複製代碼

一個具體的栗子:編輯器

const boolArray: boolean[];

boolArray = [true, false];
console.log(boolArray[0]); // true
console.log(boolArray.length); // 2

boolArray[1] = true;
boolArray = [false, false];

boolArray[0] = 'false'; // Error
boolArray = 'false'; // Error
boolArray = [true, 'false']; // Error
複製代碼

4.元組

元組類型容許表示一個已知元素數量和類型的數組,各元素的類型沒必要相同。好比,你能夠定義一對值分別爲stringnumber類型的元組。ide

// 聲明一個元組類型
let x: [string, number];
// 初始化賦值
x = ['hello', 10]; // OK
// 初始化錯誤
x = [10, 'hello']; // Error
複製代碼

當訪問一個已知索引的元素,會獲得正確的類型:函數

console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'
複製代碼

????????? 當訪問一個越界的元素,會使用聯合類型替代:

x[3] = 'world'; // OK, 字符串能夠賦值給(string|number)類型
複製代碼

可是我在編輯器裏使用的時候是報錯的,Index '2' is out-of-bounds in tuple of length 2,我也不知道爲何??

5. 枚舉

enum 類型是對JavaScript 標準數據類型的一個補充。使用枚舉類型能夠爲一組數值賦予友好的名字。

enum Color {Red, Green, Blue}
let c: Color = Color.Green;
複製代碼

默認狀況下,從0開始爲元素編號。你也能夠手動的指定成員的編號。例如,咱們將上面的栗子改爲從1開始編號:

enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;
複製代碼

或者採用所有手動賦值:

enum Color {Red = 1,Geeen = 2, Blue = 4}
let c: Color = Color.Green
複製代碼

編譯後的js

var Color;
(function (Color) {
    Color[Color["Red"] = 1] = "Red";
    Color[Color["Green"] = 2] = "Green";
    Color[Color["Blue"] = 4] = "Blue";
})(Color || (Color = {}));
var c = Color.Green;
複製代碼

全部的表達式都有返回值,它的返回值就是等號右邊的賦值。

枚舉類型提供的一個遍歷是你能夠由枚舉的值獲得它的名字。 例如,咱們知道數值爲2,可是不肯定它映射到Color裏的哪一個名字,咱們能夠查找相應的名字:

enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];

console.log(colorName);  // 顯示'Green'由於上面代碼裏它的值是2
複製代碼

這個試了一下不給數值number,發現沒辦法經過這個取值

enum Color {Red = 'r', Green = 'g', Blue = 'b'}
let c: Color = Color.Green;
console.log(c) // g
let colorName:string = Color[2]
console.log(colorName) // undefined
複製代碼

咱們看一下編譯後的javascript代碼就明白了:

var Color;
(function (Color) {
   Color["Red"] = "r";
   Color["Green"] = "g";
   Color["Blue"] = "b";
})(Color || (Color = {}));
var c = Color.Green;
console.log(c);
var colorName = Color[2];
console.log(colorName);
複製代碼

6. 特殊類型

6.1 any

any類型在 TypeScript類型系統中佔有特殊的地位。它提供給你一個類型系統的【後門】,TypeScript將會把類型檢查關閉。在類型系統裏any可以兼容全部的類型(包括它本身)。所以,全部的類型都可以被賦值給它。它也能被賦值給其餘任何類型。

let power: any;
//賦值任意類型
power = '123'
power = 123
let num:number;
num = power;
power = num;
複製代碼

6.2 null 和 undefined

在類型系統中,JavaScript 中的 null 和 undefined 字面量和其餘被標註了 any 類型的變量同樣,都能被賦值給任意類型的變量,以下例子所示:

let num: numer;
let str: string;

// 這些類型能被賦予
num = null;
str = undefined;
複製代碼

6.3 void

使用 :void來表示一個函數沒有一個返回值

function log(message:string):void{
    console.log(message)
}
複製代碼

聲明一個void類型的變量沒有什麼大用,由於你只能爲它賦予undefinednull

let unusable:void = undefined
複製代碼

7.泛型

軟件工程中,咱們不只要建立一致的定義良好的API,同時也要考慮可重用性。

不只可以支持當前的數據類型,同時也能支持將來的數據類型,這在建立大型系統時爲你提供了十分靈活的功能。

舉一個簡單的演變例子:

當不實用泛型的時候,你的代碼多是像這樣:

function identity(arg:number):number{
    return arg;
}
複製代碼

這個函數能夠接收number 類型,返回的也是number類型,考慮到這個函數的可重用性,或者,咱們可使用any類型來定義函數像這樣:

function identity(arg:any):any{
    return arg;
}
複製代碼

使用any類型會致使這個函數能夠接受任何類型的arg參數,且任何類型的值均可能被返回。

咱們須要一種方法使返回值的類型與傳入參數的類型是相同的。 接下來咱們能夠把代碼像下面這樣:

function identity<T>(arg:T):T{
    return arg
}
複製代碼

咱們給 identity 添加了類型變量 T。 若是傳入的類型(好比:number),咱們就能夠知道返回的類型也是number。如今咱們就能夠知道參數類型與返回值類型是相同的了。這有助於咱們跟蹤函數裏使用的類型的信息。

咱們把這個版本的identity函數叫作 泛型。 它可使用與多個類型,不一樣於使用any,的不肯定性,還能保持像第一個例子同樣的準確性參數類型返回值類型相同的。

7.1 泛型的使用

咱們定義了泛型函數之火,可使用兩種方法使用。

第一種是傳入全部的參數,包含類型參數:

let output = identity<string>('myString');//type of output will be 'string'
複製代碼

這裏咱們明確的指定了Tstring類型,並作爲一個參數傳給函數,而且肯定output的類型是string.

注意⚠️:使用<>不是()

第二種方法,利用類型推論--即編譯器會根據傳入的參數自動的幫助咱們肯定T的類型。

let output = identity("myString");// type of output will be 'string'
複製代碼

這裏並無使用(<>)來明確地傳入類型;編譯器能夠查看myString的值,而後把T設置爲它的類型。

7.2 使用泛型變量

使用泛型建立像identity這樣的泛型函數時,編譯器要求你在函數體必須正確的使用這個通用的類型。換句話說,你必須把這些參數看成時任意或全部類型。

看下咱們以前寫的泛型例子:

function identity<T>(arg:T):T{
    return arg
}
複製代碼

當咱們試圖在函數內輸出arg.length的時候,編譯器就會報錯類型「T」上不存在屬性「length」

function idenfity<T>(arg:T):T{
 console.log(arg.length) // Error: T doesn't have .length return arg } 複製代碼

注意⚠️:

類型變量T表明的時任意類型,因此使用這個函數的人可能傳入的是個數字,而數字時沒有.length屬性的。

而後咱們假設想操做T類型的數組而不直接是T,因爲咱們操做的是數組,此時.length的屬性應該是存在。

代碼像這樣:

function loggingIdentity<T>(arg: T[]):T[]{
console.log(arg.length) // Array has a .length,so no more error
return arg
}
複製代碼

咱們能夠這樣理解loggingIdentity的類型:泛型函數loggingIdentity,接收類型參數T和參數arg,它是個元素類型是T的數組,並返回元素類型T的數組。若是咱們傳入數字數組,將返回一個數字數組,由於此時T的類型爲number。這可讓咱們把泛型變量T看成類型的一部分使用,而不是整個類型,增長了靈活性。

咱們還能夠這樣實現上面的例子:

function loggingIdentity<T>(arg:Array<T>):Array<T>{
console.log(arg.length)
return arg
}
複製代碼

在計算機科學中,許多算法和數據結構並不會依賴於對象的實際類型。然而,你仍然會想在每一個變量裏強制提供約束。

例如:在一個函數中,它接受一個列表,而且返回這個列表的反向排序,這裏的約束是指傳入至函數的參數與函數的返回值:

function reverse<T>(items:T[]):T[]{
  const toreturn = []
  for(let i = items.length - 1;i>=0;i--){
    toreturn.push(items[i])
  }
  return toreturn
}

const sample = [1,2,3]
let reversed = reverse(sample)

console.log(reversed)

reversed[0] = '1'; // Error
reversed = ['1', '2']; // Error

reversed[0] = 1; // ok
reversed = [1, 2]; // ok
複製代碼

這個栗子中,函數reverse接受一個類型爲T的數組items:T[],返回值爲類型T的一個數組(注意:T[]),函數 reverse的返回值類型與它接受的參數類型同樣。當你傳入var sample = [1, 2, 3]時,TypeScript能推斷出 reversenumber[]類型,從而能給你類型安全。於此類似,當你傳遞一個string[]類型的數組時,TypeScript能推斷出爲string[]類型, 如:

const strArr = ['1','2']
let reversedStrs = reverse(strArr)
reversedStrs = [1, 2]; // Error
複製代碼

若是你的數組是const sample = [1,false,3]等同於const sample: (number | boolean)[],因此下面的代碼也能能夠的,若是是數組裏有兩種甚至三種類型的時候,它是可以推斷出(number | boolean)這種或的類型的。也就是下面所說的聯合類型。

function reverse<T>(items:T[]):T[]{
  const toreturn = []
  for(let i = items.length - 1;i>=0;i--){
    toreturn.push(items[i])
  }
  return toreturn
}

const sample = [1,false,3]
let reversed = reverse(sample)

console.log(reversed)

reversed[0] = true; // OK
reversed = [3, true]; // OK

reversed[0] = 1; // ok
reversed = [1, 2]; // ok
複製代碼

8.聯合類型

聯合類型表示取值能夠爲多種類型中的一種。

8.1簡單的例子

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven'
myFavoriteNumber = 7
複製代碼
let myFavoriteNumber: string | number;
myFavoriteNumber = true;

// index.ts(2,1): error TS2322: Type 'boolean' is not assignable to type 'string | number'.
//   Type 'boolean' is not assignable to type 'number'

複製代碼

聯合類型使用|分隔每一個類型。

這裏的let myFavoriteNumber: string | number;的含義是,容許myFavoriteNumber的類型是string或者number,可是不能是其餘類型。

8.2 訪問聯合類型的屬性或方法

TypeScript 不肯定一個聯合類型的變量究竟是哪一個類型的時候,咱們只能訪問此聯合類型的全部類型裏的共有的屬性或方法

function getLength(something:string|number):number{
return something.length
}
// Error 類型「string | number」上不存在屬性「length」。
  類型「number」上不存在屬性「length」。
複製代碼

上例中,length不是stringnumber的共有屬性,因此會報錯。

訪問stringnumber的共有屬性是沒問題的:

function getLength(something:string|number):string{
    return something.toString()
}
複製代碼

聯合類型的變量在被賦值的時候,會根據類型推論的規則推斷出一個類型:

let myFavoriteNumber:string|number;
myFavoriteNumber = 'seven';
console.log(myFavoriteNumber.length)

myFavoriteNumber = 7
console.log(myFavoriteNumber.length) // Rrror 類型「number」上不存在屬性「length」。

複製代碼

上例中,第二行的myFavoriteNumber被推斷成了string,訪問它的length屬性不會報錯。而第四行的myFavoriteNumber被推斷成了number,訪問它的length屬性時就報錯了。

一個常見的用例是一個能夠接受單個對象或者對象數組的函數:

function formatCommandline(command: string[] | string) {
  let line = '';
  if (typeof command === 'string') {
    line = command.trim();
  } else {
    line = command.join(' ').trim();
  }

  // Do stuff with line: string
}
複製代碼
相關文章
相關標籤/搜索