一、基礎類型
1、布爾類型boolean:
let isDone: boolean = false;
2、數字類型number
3、字符類型string
4、Symbol 類型
const sym = Symbol(); let obj = { [sym]: "***", };
5、數組類型(無關鍵字):
TypeScript像JavaScript一樣可以操作數組元素。 有兩種方式可以定義數組。
(1)第一種,可以在元素類型后面接上 []
,表示由此類型元素組成的一個數組:
// 定義存儲各種類型數據的數組時
let arrayList: any[] = [1, false, 'any'];
(2)第二種方式是使用數組泛型,Array<元素類型>
:
let list: Array<number> = [1, 2, 3];
6、元組 Tuple:
元組類型用來表示已知元素數量和類型的數組,各元素的類型不必相同,對應位置的類型需要相同。
使用元組時,必須提供每個屬性的值。在元組初始化的時候,我們還必須提供每個屬性的值,不然也會出現錯誤。
let x: [string, number]; x = ['Runoob', 1]; // 運行正常
x = [1, 'Runoob']; // 報錯
console.log(x[0]); // 輸出 Runoob
// 當訪問一個已知索引的元素,會得到正確的類型:
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)類型
console.log(x[5].toString()); // OK, 'string' 和 'number' 都有 toString
x[6] = true; // Error, 布爾不是(string | number)類型
7、枚舉 enum:枚舉類型用於定義數值集合。
enum 類型是對JavaScript標准數據類型的一個補充。 像C#等其它語言一樣,使用枚舉類型可以為一組數值賦予友好的名字。關於枚舉的內容見這篇博客:《淺析TypeScript中const和readonly的區別、枚舉和常量枚舉的區別以及關於typescript中枚舉的相關知識》
enum Color {Red, Green, Blue}; let c: Color = Color.Blue; console.log(c); // 輸出 2
8、any 類型
有時候,我們會想要為那些在編程階段還不清楚類型的變量指定一個類型。 這些值可能來自於動態的內容,比如來自用戶輸入或第三方代碼庫。 這種情況下,我們不希望類型檢查器對這些值進行檢查而是直接讓它們通過編譯階段的檢查。 那么我們可以使用 any
類型來標記這些變量。
在 TypeScript 中,任何類型都可以被歸為 any 類型。這讓 any 類型成為了類型系統的頂級類型(也被稱作全局超級類型)。
any
類型本質上是類型系統的一個逃逸艙。作為開發者,這給了我們很大的自由:TypeScript 允許我們對 any
類型的值執行任何操作,而無需事先執行任何形式的檢查。比如:
let value: any; value.foo.bar; // OK
value.trim(); // OK
value(); // OK
new value(); // OK
value[0][1]; // OK
any
類型,可以很容易地編寫類型正確但在運行時有問題的代碼。如果我們使用 any
類型,就無法使用 TypeScript 提供的大量的保護機制。為了解決 any
帶來的問題,TypeScript 3.0 引入了 unknown
類型。
9、Unknown 類型
就像所有類型都可以賦值給 any
,所有類型也都可以賦值給 unknown
。這使得 unknown
成為 TypeScript 類型系統的另一種頂級類型(另一種是 any
)。下面我們來看一下 unknown
類型的使用示例:
let value: unknown; value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = Math.random; // OK
value = null; // OK
value = undefined; // OK
value = new TypeError(); // OK
value = Symbol("type"); // OK
對 value
變量的所有賦值都被認為是類型正確的。但是,當我們嘗試將類型為 unknown
的值賦值給其他類型的變量時會發生什么?
let value: unknown; let value1: unknown = value; // OK
let value2: any = value; // OK
let value3: boolean = value; // Error
let value4: number = value; // Error
let value5: string = value; // Error
let value6: object = value; // Error
let value7: any[] = value; // Error
let value8: Function = value; // Error
unknown
類型只能被賦值給 any
類型和 unknown
類型本身。直觀地說,這是有道理的:只有能夠保存任意類型值的容器才能保存 unknown
類型的值。畢竟我們不知道變量 value
中存儲了什么類型的值。現在讓我們看看當我們嘗試對類型為 unknown
的值執行操作時會發生什么。以下是我們在之前 any
章節看過的相同操作:
let value: unknown; value.foo.bar; // Error
value.trim(); // Error
value(); // Error
new value(); // Error
value[0][1]; // Error
將 value
變量類型設置為 unknown
后,這些操作都不再被認為是類型正確的。通過將 any
類型改變為 unknown
類型,我們已將允許所有更改的默認設置,更改為禁止任何更改。
10、void 類型:用於標識方法返回值的類型,表示該方法沒有返回值。
某種程度上來說,void 類型像是與 any 類型相反,它表示沒有任何類型。當一個函數沒有返回值時,你通常會見到其返回值類型是 void
// 聲明函數返回值為void
function warnUser(): void { console.log("This is my warning message"); }
需要注意的是,聲明一個 void 類型的變量沒有什么作用,因為在嚴格模式下,它的值只能為 undefined
:
11、Null (表示對象值缺失)和 Undefined (用於初始化變量為一個未定義的值)類型
TypeScript 里,undefined
和 null
兩者有各自的類型分別為 undefined
和 null
。和 void
相似,它們的本身的類型用處不是很大:
let u: undefined = undefined; let n: null = null;
默認情況下null
和undefined
是所有類型的子類型。 就是說你可以把 null
和undefined
賦值給number
類型的變量。
然而,當你指定了--strictNullChecks
標記,null
和undefined
只能賦值給void
和它們各自。 這能避免很多常見的問題。 也許在某處你想傳入一個 string
或null
或undefined
,你可以使用聯合類型:string | null | undefined
。
12、object, Object 和 {} 類型
(1)object 類型是:TypeScript 2.2 引入的新類型,它用於表示非原始類型。也就是除number
,string
,boolean
,symbol
,null
或undefined
之外的類型。
使用object
類型,就可以更好的表示像Object.create
這樣的API。例如:
declare function create(o: object | null): void; create({ prop: 0 }); // OK
create(null); // OK
create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error
(2)Object 類型:Object 類型是所有 Object 類的實例的類型。
它由以下兩個接口來定義:
2.1:Object 接口定義了 Object.prototype 原型對象上的屬性;
// node_modules/typescript/lib/lib.es5.d.ts
interface Object { constructor: Function; toString(): string; toLocaleString(): string; valueOf(): Object; hasOwnProperty(v: PropertyKey): boolean; isPrototypeOf(v: Object): boolean; propertyIsEnumerable(v: PropertyKey): boolean; }
2.2:ObjectConstructor 接口定義了 Object 類的屬性
// node_modules/typescript/lib/lib.es5.d.ts
interface ObjectConstructor { /** Invocation via `new` */
new(value?: any): Object; /** Invocation via function calls */ (value?: any): any; readonly prototype: Object; getPrototypeOf(o: any): any; // ···
} declare var Object: ObjectConstructor;
Object 類的所有實例都繼承了 Object 接口中的所有屬性。
(3){} 類型:{} 類型描述了一個沒有成員的對象。當你試圖訪問這樣一個對象的任意屬性時,TypeScript 會產生一個編譯時錯誤。
但是,你仍然可以使用在 Object 類型上定義的所有屬性和方法,這些屬性和方法可通過 JavaScript 的原型鏈隱式地使用:
// Type {}
const obj = {}; // "[object Object]"
obj.toString();
13、never,代表不會出現的值。這意味着聲明為 never 類型的變量只能被 never 類型所賦值,在函數中它通常表現為拋出異常或無法執行到終止點(例如無限循環)
// 返回never的函數必須存在無法達到的終點
function error(message: string): never { throw new Error(message); } function infiniteLoop(): never { while (true) {} }
在 TypeScript 中,可以利用 never 類型的特性來實現全面性檢查,具體示例如下:
type Foo = string | number; function controlFlowAnalysisWithNever(foo: Foo) { if (typeof foo === "string") { // 這里 foo 被收窄為 string 類型
} else if (typeof foo === "number") { // 這里 foo 被收窄為 number 類型
} else { // foo 在這里是 never
const check: never = foo; } }
注意在 else 分支里面,我們把收窄為 never 的 foo 賦值給一個顯示聲明的 never 變量。如果一切邏輯正確,那么這里應該能夠編譯通過。但是假如后來有一天你的同事修改了 Foo 的類型:
type Foo = string | number | boolean;
然而他忘記同時修改 controlFlowAnalysisWithNever
方法中的控制流程,這時候 else 分支的 foo 類型會被收窄為 boolean
類型,導致無法賦值給 never 類型,這時就會產生一個編譯錯誤。通過這個方式,我們可以確保 controlFlowAnalysisWithNever
方法總是窮盡了 Foo 的所有可能類型。
通過這個示例,我們可以得出一個結論:使用 never 避免出現新增了聯合類型沒有對應的實現,目的就是寫出類型絕對安全的代碼
二、TypeScript 斷言
1、類型斷言
有時候你會遇到這樣的情況,你會比 TypeScript 更了解某個值的詳細信息。通常這會發生在你清楚地知道一個實體具有比它現有類型更確切的類型。
通過類型斷言這種方式可以告訴編譯器,“相信我,我知道自己在干什么”。類型斷言好比其他語言里的類型轉換,但是不進行特殊的數據檢查和解構。它沒有運行時的影響,只是在編譯階段起作用。TypeScript會假設你,程序員,已經進行了必須的檢查。
類型斷言有兩種形式:
// 一是“尖括號”語法:
let someValue: any = "this is a string"; let strLength: number = (<string>someValue).length; // 另一個為as語法:
let someValue: any = "this is a string"; let strLength: number = (someValue as string).length;
兩種形式是等價的。 至於使用哪個大多數情況下是憑個人喜好;然而,當你在TypeScript里使用JSX時,只有 as
語法斷言是被允許的。
在上下文中當類型檢查器無法斷定類型時,一個新的后綴表達式操作符 !
可以用於斷言操作對象是非 null 和非 undefined 類型。具體而言,x! 將從 x 值域中排除 null 和 undefined 。
那么非空斷言操作符到底有什么用呢?下面我們先來看一下非空斷言操作符的一些使用場景。
function myFunc(maybeString: string | undefined | null) { const onlyString: string = maybeString; // Error const ignoreUndefinedAndNull: string = maybeString!; // Ok }
(2)調用函數時忽略 undefined 類型
type NumGenerator = () => number; function myFunc(numGenerator: NumGenerator | undefined) { // Object is possibly 'undefined'.(2532) // Cannot invoke an object which is possibly 'undefined'.(2722) const num1 = numGenerator(); // Error const num2 = numGenerator!(); //OK }
3、確定賦值斷言
在 TypeScript 2.7 版本中引入了確定賦值斷言,即允許在實例屬性和變量聲明后面放置一個 !
號,從而告訴 TypeScript 該屬性會被明確地賦值。為了更好地理解它的作用,我們來看個具體的例子:
let x: number; initialize(); // Variable 'x' is used before being assigned.(2454) console.log(2 * x); // Error function initialize() { x = 10; }
很明顯該異常信息是說變量 x 在賦值前被使用了,要解決該問題,我們可以使用確定賦值斷言:
let x!: number; initialize(); console.log(2 * x); // Ok function initialize() { x = 10; }
通過 let x!: number;
確定賦值斷言,TypeScript 編譯器就會知道該屬性會被明確地賦值。
三、變量聲明
1、ts 的變量命名規則:不能以數字開頭,不能包含下划線_和美元符號$之外的特殊字符
2、聲明變量方式:
let [變量名] : [類型] = 值; let [變量名] : [類型]; let [變量名] = 值; let [變量名];
4種方式,沒有類型那么類型就是any;沒有值,那么值就是undefined。