TypeScript 初學者入門學習筆記(一)

來源 | https://www.cnblogs.com/echoyya/p/14542005.html
什么是TypeScript?
是添加了類型系統(tǒng)的 JavaScript,適用于任何規(guī)模的項目。 是一門靜態(tài)類型、弱類型的語言。 完全兼容 JavaScript,且不會修改 JavaScript 運行時的特性。 可以編譯為 JavaScript,然后運行在瀏覽器、Node.js 等任何能運行 JavaScript 的環(huán)境中。 擁有很多編譯選項,類型檢查的嚴格程度可通過配置文件決定。 可以和 JavaScript 共存,這意味著 JavaScript 項目能夠漸進式的遷移到 TypeScript。 增強了編輯器(IDE)的功能,提供了代碼補全、接口提示、跳轉到定義、代碼重構等能力。 擁有活躍的社區(qū),大多數(shù)常用的第三方庫都提供了類型聲明,并且開源免費
JavaScript 的缺點
它沒有類型約束,一個變量可能初始化時是字符串,又被賦值為數(shù)字。 由于隱式類型轉換的存在,有些變量的類型很難在運行前就確定。 基于原型的面向對象編程,使得原型上的屬性或方法可以在運行時被修改。
為什么使用 TypeScript?
安裝
安裝:npm install -g typescript,全局安裝,可以在任意位置執(zhí)行tsc
TypeScript 的特性
1、類型系統(tǒng)
TypeScript 是靜態(tài)類型
// test.jslet foo = 1;foo.split(' ');// TypeError: foo.split is not a function 運行時會報錯(foo.split 不是一個函數(shù))
靜態(tài)類型:是指編譯階段就能確定每個變量的類型,類型錯誤往往會導致語法錯誤。
TypeScript 在運行前需要先編譯為 JavaScript,而在編譯階段就會進行類型檢查,所以 TypeScript 是靜態(tài)類型,以下代碼在編譯階段就會報錯:
// test.tslet foo: number = 1;foo.split(' ');// Property 'split' does not exist on type 'number'. 編譯時報錯(數(shù)字沒有 split 方法),無法通過編譯
TypeScript 是弱類型
類型系統(tǒng)按照是否允許隱式類型轉換分類,可以分為強類型和弱類型。
以下代碼在 JS或 TS 中都可以正常運行,運行時數(shù)字 1 會被隱式類型轉換為字符串 '1',加號 + 被識別為字符串拼接,所以打印出結果是字符串 '11'。
console.log(1 + '1'); // 11TS 是完全兼容 JS的,并不會修改 JS 運行時的特性,所以它們都是弱類型。雖然 TS 不限制加號兩側的類型,但是可以借助類型系統(tǒng),以及 ESLint 代碼檢查,來限制加號兩側必須同為數(shù)字或同為字符串。會在一定程度上使得 TypeScript 向強類型更近一步了——當然,這種限制是可選的。
這樣的類型系統(tǒng)體現(xiàn)了 TypeScript 的核心設計理念:在完整保留 JavaScript 運行時行為的基礎上,通過引入靜態(tài)類型系統(tǒng)來提高代碼的可維護性,減少可能出現(xiàn)的 bug。
2、原始數(shù)據(jù)類型基本使用
布爾值、數(shù)值、字符串、null、undefined,為變量指定類型,且變量值需與類型一致
let flag: boolean = falselet num: number = 15let str: string = 'abc'var a: null = nullvar b: undefined = undefined// 編譯通過
使用構造函數(shù)創(chuàng)造的對象不是原始數(shù)據(jù)類型,事實上 new XXX() 返回的是一個 XXX對象:
let flag:boolean=new Boolean(false) // Type 'Boolean' is not assignable to type 'boolean'.let num: number = new Number(15) // Type 'Number' is not assignable to type 'number'.let str: string = new String('abc') // Type 'String' is not assignable to type 'string'.// 編譯通過
但是可以直接調用 XXX 也可以返回一個 xxx 類型:
let flag: boolean = Boolean(false)let num : number = Number(15)let str : string = String('abc')// 編譯通過
數(shù)值
使用 number 定義數(shù)值類型:
let decLiteral: number = 6;let hexLiteral: number = 0xf00d;let binaryLiteral: number = 0b1010; // ES6 中的二進制表示法let octalLiteral: number = 0o744; // ES6 中的八進制表示法let notANumber: number = NaN;let infinityNumber: number = Infinity;
編譯結果:
var decLiteral = 6;var hexLiteral = 0xf00d;var binaryLiteral = 10; // ES6 中的二進制表示法var octalLiteral = 484; // ES6 中的八進制表示法var notANumber = NaN;var infinityNumber = Infinity;
ES6 中二進制和八進制數(shù)值表示法:分別用前綴0b|0B和0o|0O表示。會被編譯為十進制數(shù)字。
字符串
使用string定義字符串類型:
let myName: string = 'Echoyya';let str: string = `Hello, my name is ${myName}.`; // 模板字符串
編譯結果:
var myName = 'Echoyya';var str = "Hello, my name is " + myName + "."; // 模板字符串ES6 中模板字符串:增強版的字符串,用反引號(`) 標識 ${expr} 用來在模板字符串中嵌入表達式。
空值 及(與Null 和 Undefined的區(qū)別)
JavaScript 沒有空值(Void)的概念,在 TS中,用 void 表示沒有任何返回值的函數(shù):
function alertName(): void {alert('My name is Tom');}
然而聲明一個 void 類型的變量沒什么用,因為只能將其賦值為 undefined 和 null:
let unusable: void = undefined;Null 和 Undefined
let u: undefined = undefined;let n: null = null;
區(qū)別:undefined 和 null 是所有類型的子類型。也就是說 undefined 類型的變量,可以賦值給所有類型的變量,包括 void 類型:
let num: number = undefined;let u: undefined;let str: string = u;let vo: void= u;// 編譯通過
而 void 類型的變量不能賦值給其他類型的變量,只能賦值給 void 類型:
let u: void;let num: number = u; // Type 'void' is not assignable to type 'number'.let vo: void = u; // 編譯通過
3、任意值
1)、任意值(Any)用來表示允許賦值為任意類型。一個普通類型,在賦值過程中是不被允許改變類型的,any 類型,允許被賦值為任意類型。
let str: string = 'abc';str = 123;// Type 'number' is not assignable to type 'string'.// -----------------------------------------------------------------let str: any = 'abc';str = 123;// 編譯通過
2)、任意值可以訪問任意屬性和方法:
let anyThing: any = 'hello';anyThing.setName('Jerry');anyThing.setName('Jerry').sayHello();anyThing.myName.setFirstName('Cat');console.log(anyThing.myName);console.log(anyThing.myName.firstName);// 編譯通過
3)、未聲明類型的變量:如果變量在聲明時,未指定類型,那么會被識別為任意值類型:
let something;something = 'seven';something = 7;// 等價于let something: any;something = 'seven';something = 7;
類型推論
若未明確指定類型,那么 TS 會依照類型推論(Type Inference)規(guī)則推斷出一個類型。
以下代碼雖然沒有指定類型,但編譯時會報錯:
let data = 'seven';data = 7;// Type 'number' is not assignable to type 'string'.
事實上,它等價于:
let data: string = 'seven';data = 7;// Type 'number' is not assignable to type 'string'.
TS會在未明確指定類型時推測出一個類型,這就是類型推論。如果定義時未賦值,不管之后是否賦值,都會被推斷成 any 類型:
let data;data = 'seven';data = 7;// 編譯通過
聯(lián)合類型
聯(lián)合類型(Union Types)表示取值可以為多種類型中的一種,使用 | 分隔每個類型。
let data: string | number; // 允許 data 可以是 string 或 number 類型,但不能是其他類型data = 'seven';data = 7;data = true;// Type 'boolean' is not assignable to type 'string | number'.
訪問聯(lián)合類型的屬性或方法:當不確定一個聯(lián)合類型的變量到底是哪個類型時,只能訪問此聯(lián)合類型中所有類型共有的屬性或方法:
function getLength(something: string | number): number {return something.length;}// length 不是 string 和 number 的共有屬性,所以會報錯// Property 'length' does not exist on type 'string | number'.// Property 'length' does not exist on type 'number'.
訪問 string 和 number 的共有屬性:
function getString(something: string | number): string {return something.toString();}
聯(lián)合類型的變量在被賦值的時候,會根據(jù)類型推論的規(guī)則推斷出一個類型:
let data: string | number;data = 'seven';console.log(data.length); // 5data = 7;console.log(data.length); // 編譯時報錯// Property 'length' does not exist on type 'number'.
line2:data 被推斷為 string,訪問length 屬性不會報錯。
line4:data 被推斷為 number,訪問length 屬性報錯。
對象的類型——接口
在 TS中,使用接口(Interfaces)來定義對象的類型。可用于對類的一部分行為進行抽象以外,也常用于對對象的形狀(Shape)進行描述。
interface Person {name: string;age: number;}let p1: Person = {name: 'Tom',age: 25};
定義一個接口 Person(接口一般首字母大寫),定義一個變量 tom 類型是 Person。這樣就約束了 tom 的形狀必須和接口 Person 一致。
確定屬性
確定屬性:賦值時,定義的變量的形狀必須與接口形狀保持一致。變量的屬性比接口少或多屬性都是不允許的:
interface Person {name: string;age: number;}let p1: Person = { // 缺少 age 屬性name: 'Tom'};// Type '{ name: string; }' is not assignable to type 'Person'.// Property 'age' is missing in type '{ name: string; }'.// -----------------------------------------------------------------let p2 Person = { // 多余 gender 屬性name: 'Tom',age: 25,gender: 'male'};// Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.// Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.
可選屬性
可選屬性:是指該屬性可以不存在。有時不需要完全匹配一個接口時,可以用可選屬性,但此時仍然不允許添加未定義的屬性
interface Person {name: string;age?: number;}let p1: Person = { // 編譯通過name: 'Tom'};let p2: Person = { // 編譯通過name: 'Tom',age: 25};let p3: Person = { // 報錯(同上)name: 'Tom',age: 25,gender: 'male'};
任意屬性
任意屬性:允許一個接口有任意的屬性
interface Person {name: string;age?: number;[propName: string]: any;}let p1: Person = {name: 'Tom',gender: 'male'};
使用 [propName: string] 定義了任意屬性的屬性名取 string 類型的值。屬性值為任意值
注意:一旦定義了任意屬性,那么確定屬性和可選屬性的類型都必須是它的類型的子集:
例一:任意屬性的類型是 string,但是可選屬性 age 的值卻是 number,number 不是 string 的子屬性,所以會報錯。
interface Person {name: string;age?: number;[propName: string]: string;}let p1: Person = {name: 'Tom',age: 25,gender: 'male'};// Property 'age' of type 'number' is not assignable to string index type 'string// Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.// Property 'age' is incompatible with index signature.// Type 'number' is not assignable to type 'string'
例二:一個接口中只能定義一個任意屬性。如果接口中有多個類型的屬性,可以在任意屬性中使用聯(lián)合類型:
interface Person {name: string;age?: number;[propName: string]: string | number;}let p1: Person = { // 編譯通過name: 'Tom',age: 25,gender: 'male',year:2021};
只讀屬性
對象中的一些字段只能在創(chuàng)建時被賦值,可以使用 **readonly **定義只讀屬性:
例一:使用 readonly 定義的屬性 id 初始化后,又被重新賦值,所以會報錯。
interface Person {readonly id: number;name: string;age?: number;[propName: string]: any;}let p1: Person = {id: 89757,name: 'Tom',gender: 'male'};p1.id = 9527;// Cannot assign to 'id' because it is a read-only property.
例二:只讀的約束存在于第一次給對象賦值的時候,而不是第一次給只讀屬性賦值時:
interface Person {readonly id: number;name: string;age?: number;[propName: string]: any;}let p2: Person = { // 第一次給對象賦值name: 'Tom',gender: 'male'};p2.id = 89757;// Property 'id' is missing in type '{ name: string; gender: string; }' but required in type 'Person' 對 p2 賦值時,沒有給 id 賦值// Cannot assign to 'id' because it is a read-only property. id 是只讀屬性
數(shù)組的類型
類型 + 方括號 表示法
let arr: number[] = [1, 1, 2]; // 數(shù)組元素中不允許出現(xiàn)其他的類型let arr1: number[] = [1, '1', 2]; // 報錯:Type 'string' is not assignable to type 'number'.
let arr2: number[] = [1, 1, 2, 3, 5];arr2.push('8');//報錯:Argument of type '"8"' is not assignable to parameter of type 'number'.
數(shù)組泛型
使用數(shù)組泛型(Array Generic) Array<elemType> 來表示數(shù)組:
let arr3: Array<number> = [1, 1, 2, 3, 5];泛型涉及內容較多,后期有時間會在整理一篇文章,敬請關注!
用接口表示數(shù)組
之前介紹了使用接口表示對象的類型,同樣接口也可以用來描述數(shù)組:
interface NumberArray {[index: number]: number;}let arr: NumberArray = [1, 1, 2, 3, 5];
NumberArray 表示:索引的類型是數(shù)字,值的類型也是數(shù)字,這樣便可以表示一個數(shù)字類型的數(shù)組,雖然接口也可以描述數(shù)組,但是一般不會這么做,因為這種方式較復雜。有一例外,就是常用來表示類數(shù)組。
類數(shù)組
類數(shù)組(Array-like Object)不是數(shù)組類型,比如 arguments,實際上是一個類數(shù)組,不能用普通數(shù)組的方式來描述,而應該用接口:
function sum() {let args: number[] = arguments;}// 報錯:Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.
function sum() {let args: {[index: number]: number;length: number;callee: Function;} = arguments;}
function sum() {let args: IArguments = arguments;}//其中 IArguments 是 TypeScript 中定義好了的類型,它實際上就是:interface IArguments {[index: number]: any;length: number;callee: Function;}
any 在數(shù)組中的應用
一個比較常見的做法是,用 any 表示數(shù)組中允許出現(xiàn)任意類型:
let list: any[] = ['Echoyya', 25, { website: 'https://www.cnblogs.com/echoyya/' }, false];學習更多技能
請點擊下方公眾號
![]()
