一膛腐、什么是 TypeScript
先看一下官網(wǎng)上對 TypeScript 的介紹铡俐。
關(guān)鍵詞:超集
言外之意 JavaScript 的所有用法 TypeScript 都支持结蟋。
關(guān)鍵詞:Type
TypeScript 強(qiáng)調(diào)也是其優(yōu)點(diǎn)之意是【類型】
為什么需要 Ts 呢或听?舉個(gè)小例子:
一個(gè)杯子可以裝水探孝,也可以當(dāng)筆筒裝雜物。但是誉裆,拿裝雜物的杯子些許顯得不太干凈顿颅。物有所用,各司其職
足丢。
Js 是弱類型可以定義 number 類型粱腻,可以定義 string 類型,這就并未各司其職斩跌。
所以绍些,這就誕生了 Ts
【前端經(jīng)常遇見的類型問題]
image.png
二、TypeScript 的安裝及運(yùn)行
安裝:npm install -g typescript 或者 yarn add global typescript
確認(rèn)是否安裝成功: tsc -v
在官網(wǎng)中(中文網(wǎng) -- 練習(xí))可以將 Ts 轉(zhuǎn)換為 Js
瀏覽器會識別 html / css / js耀鸦。識別不了 ts柬批,所以 ts 編譯轉(zhuǎn)變?yōu)闉?js啸澡。
https://jkchao.github.io/typescript-book-chinese/compiler/overview.html#%E6%96%87%E4%BB%B6%EF%BC%9Autilities
打開編輯器(確保在node/npm/typescript都成功的前提下)
第一步:新建文件夾 TS-APP
第二步:TS-APP下面新建一個(gè)index.html文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script src="./app.js"></script>
</body>
</html>
在<script src="./app.js"></script>若src="./app.ts"會報(bào)錯(cuò)
第三步:在TS-APP下面新建一個(gè)app.ts文件
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
let button = document.createElement('button');
button.textContent = "Say Hello";
button.onclick = function() {
alert(greeter.greet());
}
document.body.appendChild(button);
要想在瀏覽器運(yùn)行起來氮帐,需要將 app.ts 編譯轉(zhuǎn)換為 app.js嗅虏。此時(shí)就要進(jìn)行此命令: tsc app.ts
?運(yùn)行完此命令之后,會自動(dòng)生成一個(gè) app.js 文件上沐。此時(shí)瀏覽器打開皮服,效果如下:
問答區(qū):如果想同時(shí)運(yùn)行多個(gè) ts 文件該如何操作。
此時(shí)需要進(jìn)行此命令tsc --init
命令執(zhí)行成功之后参咙,會自動(dòng)生成一個(gè)tsConfig.json
文件冰更,此文件則會將幫助所有的 ts 文件編譯轉(zhuǎn)換為相應(yīng)的 js 文件。
?運(yùn)行所有 ts 文件昂勒,tsc
即可蜀细,則會全部轉(zhuǎn)換
在此處,插播推薦兩個(gè)插件:
Live Server(能夠保存的時(shí)候戈盈,自動(dòng)更改)
TypeScript Auto Compiler(能夠保存之后奠衔,不需要執(zhí)行 tsc
命令,自動(dòng)會生成相應(yīng)的 js 文件)
三塘娶、TypeScript 入門
(一)TypeScript -- 基本數(shù)據(jù)類型和報(bào)錯(cuò)解析
為了讓程序有價(jià)值归斤,我們需要能夠處理最簡單的數(shù)據(jù)單元:數(shù)字,字符串刁岸,結(jié)構(gòu)體脏里,布爾值等。 TypeScript支持與JavaScript幾乎相同的數(shù)據(jù)類型虹曙,此外還提供了實(shí)用的枚舉類型方便我們使用迫横。
// 基本數(shù)據(jù)類型
let num = 25;
let float = 25.5;
let hex = 0xf000; // 16進(jìn)制
let binary = 0b1001; // 2進(jìn)制
let octal = 0o733; // 8進(jìn)制
// 重新賦值
num = '25'; // 會報(bào)錯(cuò),可以在終端編寫 tsc 進(jìn)行查看錯(cuò)誤?酝碳,如下圖7.
// ts原型
let num = 25;
// 等同于
// let num: number = 25;
其他數(shù)據(jù)類型:string 矾踱、boolean 、 any
??注意:在如下例子中:anything 賦予任何值都不會報(bào)錯(cuò)疏哗,是因?yàn)橹皇墙o anything 開辟了一個(gè)空間呛讲,但是并沒有定義其類型。在項(xiàng)目中返奉,盡量避免需用 any贝搁,不太利于后期維護(hù)。
// boolean
// ts原型
let isLogin = false;
// 等同于
// let isLogin: string = false;
// **----------------------** //
// string
let str: string = 'hello Tc';
// **----------------------** //
// any
let anything; // 等同于 let anything: any
// 沒報(bào)錯(cuò)的原因芽偏,只是開辟了一個(gè)空間雷逆,但是沒有定義其類型
anything = 25;
anything = 'hello'
(二)TypeScript -- 數(shù)組 元組 枚舉
// 數(shù)組 元組 枚舉
let names: Array<string> = ['hello', 'word'];
// console.log(names[0]); // hello
// names[0] = 100; // 報(bào)錯(cuò),不能將類型“100”分配給類型“string”哮针。
// names[0] = 'yes';
let number: number[] = [1, 2, 3];
let anyArray: any[] = [1, 'hello', true];
// **----------------------** //
// 元組
let colors: [string, number] = ['hello', 99];
// let colors: [string, number] = [99, 'hello']; // 會報(bào)錯(cuò)
// **----------------------** //
// 枚舉
enum Color{
Black,
Yellow,
Red,
}
// let myColor: Color = Color.Black; // 輸出為 0
// let myColor: Color = Color.Yellow; // 輸出為 1
// 若Yellow = 100, 則Red為101
此枚舉類型关面,轉(zhuǎn)換為 js 的時(shí)候是函數(shù)形式
var Color;
(function (Color) {
Color[Color["Black"] = 0] = "Black";
Color[Color["Yellow"] = 1] = "Yellow";
Color[Color["Red"] = 2] = "Red";
})(Color || (Color = {}));
// let myColor: Color = Color.Black; // 輸出為 0
// let myColor: Color = Color.Yellow; // 輸出為 1
// 若Yellow = 100, 則Red為101
控制臺輸出:{0: "Black", 1: "Yellow", 2: "Red", Black: 0, Yellow: 1, Red: 2}
(三)函數(shù)相關(guān)類型
// 函數(shù)的相關(guān)類型
function returnValue() {
return 'hello';
}
// console.log(returnValue()); // hello
// 規(guī)范寫法
function returnNum(): number { // 定義其返回值類型
return 520;
}
// **----------------------** //
// 若函數(shù)返回值 -- 空
function sayHello(): void {
console.log('hello @@@@@');
}
sayHello();
// **----------------------** //
// 參數(shù)類型
// 不標(biāo)準(zhǔn)寫法,已經(jīng)知道value1和value2的類型坦袍。
// 報(bào)錯(cuò)內(nèi)容:參數(shù)“value1”隱式具有“any”類型。
// function sumVal(value1, value2) {
// return value1 + value2;
// }
function sumVal1(value1: number, value2: number): number {
return value1 + value2;
// return value1 * value2; // 如果兩個(gè)參數(shù)中有一個(gè)不是數(shù)值 那么返回的是NAN等太。但是若為0或者''捂齐,返回0。
}
console.log(sumVal1(1, 2)); // 3
// console.log(sumVal1(1, ''));
function sumVal2(value1: number, value2: string): string {
return value1 + value2;
}
console.log(sumVal2(1, 'hello')); // 1hello
// **----------------------** //
// 函數(shù)類型
let myFunc: (a: number, b: number) => number;
// 若函數(shù)為定義類型缩抡,如下則都可以賦值不同類型奠宜。
// myFunc = sayHello;
// myFunc();
myFunc = sumVal1; // 將函數(shù) sumVal1 賦予給 myFunc
console.log(myFunc(5, 5)); // 10
??注意:在最開始入 ts 坑時(shí),除開會寫 any 之外瞻想。印象最深刻的是 void压真。fuc(): void或者fuc: () => void。類似這樣的寫法都沒少寫蘑险。但是滴肿,碰到 .then。一報(bào)錯(cuò)就改成
any
佃迄。
void 其實(shí)是代表函數(shù)返回值為空
時(shí)才使用的泼差。所以,不言而喻呵俏,.then 為什么會報(bào)錯(cuò)了堆缘。
??any,有人開玩笑說:把 TS 用成 AnyScript 的人開除普碎。如果項(xiàng)目中經(jīng)常使用any吼肥,則失去了TS本身最大的意義
(四)對象 object & type
// 對象 object & type
let listObj = {
name: 'Danile',
age: 31
};
// 不正確寫法
// 報(bào)錯(cuò)內(nèi)容:需要去包含 name 和 age 兩個(gè)屬性
// listObj = {};
// 報(bào)錯(cuò)內(nèi)容:不能包含別的屬性
// listObj = {
// n: 'hello',
// a: 12,
// }
// 最規(guī)范寫法
// let listObj(name: string, age: number) = {
// name: 'Danile',
// age: 31
// };
// **----------------------** //
// 稍微復(fù)雜對象類型
let complex: { data: number[], myFunc: (item: number) => number[] } = {
data: [1, 2, 3],
myFunc: function(item: number): number[] {
this.data.push(item);
return this.data;
}
}
// console.log(complex.myFunc(520));
// **----------------------** //
// type 生成類型
// type MyType = { data: number[], myFunc: (item: number) => number[] };
interface MyType { data: number[], myFunc: (item: number) => number[] };
let complex2: MyType = {
data: [1, 2, 3],
myFunc: function(item: number): number[] {
this.data.push(item);
return this.data;
}
}
console.log(complex2.myFunc(520));
?type 和 interface 的區(qū)別?
- 其中 interface 可以如下合并多個(gè)麻车,而 type 只能使用 & 類進(jìn)行連接缀皱。
interface A { a: number; } interface A { b: number; } const a: A = { a: 3, b: 4 }
interface 可以繼承 , type 不可以繼承
(type已經(jīng)可以繼承绪氛、實(shí)現(xiàn)了唆鸡。是2.X版本改的)
(五)union type(聯(lián)合類型) 檢查類型 null undefined never
// union type 檢查類型 null undefined never
// union type
let unionType: number | string | boolean = 12;
unionType = '12';
unionType = true;
// 檢查類型
let checkType = 10;
if (typeof checkType == 'number') {
console.log('number')
}
// null 和 undefined
// let myNull = 12;
// myNull = null; // 如果"strict": false的時(shí)候則不會有問題
let myNull = null;
myNull = undefined;
// never
// never類型是任何類型的子類型涝影,也可以賦值給任何類型枣察;然而,沒有類型是never的子類型或可以賦值給never
// 類型(除了never本身之外)燃逻。及時(shí)any也不可以賦值給never序目。通常表現(xiàn)為拋出異常或無法執(zhí)行到終點(diǎn)(例如無限循環(huán))
let x: never;
// x = 123; // 報(bào)錯(cuò):不能將類型“123”分配給類型“never”伯襟。
// never的應(yīng)用場景 拋出異常
function error(message: string): never {
throw new Error(message);
}
// 死循環(huán)
function loop(): never {
while (true) {}
}
let y: number;
y = ( () => {
throw new Error('message');
})();
字面量類型:字面量也就是 JavaScript 基元類型具體的值猿涨。而在 TypeScript 中,我們可以將字面量作為一種
自定義的類型
姆怪,這種類型被稱為字面量類型type China = 'China'; let country: China = 'China'; // ok country = 'America'; // error: Type '"America"' is not assignable to type '"China"'.
null 和 undefined
當(dāng)你指定了--strictNullChecks標(biāo)記叛赚,null和undefined
只能賦值給void和它們各自
澡绩。 這能避免 很多常見的問題(防止項(xiàng)目aa.bb 取不到值等情況)。
(六)class 類(屬性俺附,方法)| 繼承
?區(qū)分 public protected private 的區(qū)別
public: 公共的
private: 當(dāng)成員被標(biāo)記成 private時(shí)肥卡,它就不能在聲明它的類的外部訪問。
protected: protected修飾符與 private修飾符的行為很相似事镣,但有一點(diǎn)不同步鉴, protected成員在派生類中仍然可以訪問
// class 類(屬性,方法)
class Person {
public name: string;
protected gender: string;
private age: number = 27;
// public username: string;
constructor(name: string, gender: string, public username: string) {
this.name = name;
this.username = username;
this.gender = gender;
}
printAge(age: number) {
this.age = age;
console.log(this.age);
}
setGender(gender: string) {
this.gender = gender;
console.log(this.gender);
}
}
const person = new Person('Danile', '女', 'Ts');
console.log(person.name, person.username);
person.printAge(30);
person.setGender('男');
// **----------------------** //
// 繼承
// 子類可以獲得父類所有公開的
class Studen extends Person {
studentId: number;
constructor(name: string, username: string, studentId: number) {
super(name, username);
this.studentId = studentId;
}
}
const student = new Studen('Kris', 'Ts', 23);
console.log(student.name, student.username, student.studentId);
console.log(student)
把類當(dāng)做接口使用
類定義會創(chuàng)建兩個(gè)東西:類的實(shí)例類型和一個(gè)構(gòu)造函數(shù)璃哟。 因?yàn)轭惪梢詣?chuàng)建出類型氛琢,所以你能夠在允許使用接口的地方使用類。
x: number; y: number; } interface Point3d extends Point { z: number; } let point3d: Point3d = {x: 1, y: 2, z: 3};
(七) class set get修飾詞 用于隔離私有屬性和可公開屬性
// 1.class set get修飾詞 用于隔離私有屬性 和 可公開屬性
// 2.class 靜態(tài)屬性和方法
class Person1 {
private _name: string = 'Danile_getName';
// 私用屬性賦值
set setName(value: string) {
this._name = value;
}
// 私有屬性取值
get getName() {
return this._name;
}
}
let person1 = new Person1();
console.log(person1.getName); // Danile_getName
person1.setName = 'Danile_setName';
console.log(person1.getName); // Danile_setNames
對于 set 和 get 還可以應(yīng)用在校驗(yàn)等場景随闪⊙羲疲看如下圖:
(八)namespace 命名空間
// namespace 命名空間
namespace myMath {
export const PI = 3.14;
export function sumValue(num1: number, num2: number): number {
return num1 + num2;
}
export function calcCircle(value: number) {
return value * PI
}
}
const PI = 2.88;
console.log(myMath.sumValue(5, 10)); // 10
console.log(myMath.PI); // 3.14
console.log(PI); // 2.88
命名空間最大的好處是:隔離環(huán)境變量的污染
若將命名空間放不到不同的文件夾,該如何铐伴?
// sumValue.ts
namespace myMath {
export function sumValue(num1: number, num2: number): number {
return num1 + num2;
}
}
// calcCircle.ts
namespace myMath {
export const PI = 3.14;
export function calcCircle(value: number) {
return value * PI
}
}
// nameSpace.ts
console.log(myMath.sumValue); // 會報(bào)錯(cuò)的障般,找不到myMath的文件
console.log(myMath.calcCircle);
// 解決方案1: 在app.ts(最外層入口)加,<script src="sumValue.js"></script>
// 解決方案2: tsc --outfile app.js sumValue.ts calcCircle.ts app.ts
// app.js 是輸入文件
// sumValue.ts calcCircle.ts app.ts 合并為app.js文件
謹(jǐn)慎使用 --outFile
- 運(yùn)行時(shí)的錯(cuò)誤盛杰;
- 快速編譯挽荡;
- 全局作用域;
- 難以分析即供;
- 難以擴(kuò)展定拟;
- _references;
- 代碼重用逗嫡;
- 多目標(biāo)青自;
- 單獨(dú)編譯;
相關(guān)鏈接:https://jkchao.github.io/typescript-book-chinese/tips/outFileCaution.html#%E8%BF%90%E8%A1%8C%E6%97%B6%E7%9A%84%E9%94%99%E8%AF%AF
多重命名空間
// 多重命名空間
namespace myMath {
export namespace Circle {
export const PI = 3.14;
export function sumValue(num1: number, num2: number): number {
return num1 + num2;
}
export function calcCircle(value: number) {
return value * PI
}
}
}
// 若 namespace Circle 不進(jìn)行export驱证。則myMath.Circle取不到延窜。
console.log(myMath.Circle.sumValue(5, 10));
reference -- 引入ts文件,寫法: ///
// 引入文件
// 它是一種注釋抹锄,告訴typescript編譯器逆瑞,當(dāng)前文件使用了哪些聲明文件,以幫助編輯器提示信息伙单,
// 及編譯器檢查類型获高。這種注釋很重要,如果后面的路徑不對吻育,則編譯會失敗念秧。
/// <reference path="text_1.ts" />
// **----------------------** //
// 多個(gè)文件打包
tsc --outFile app.ts
(九)interface
// interface 接口
interface PersonInterface {
name: string,
ages: number, // :號,是必傳
sex?: string, // 布疼?:,是非必傳
readonly salary: number, // 只讀不能修改, personInfo.salary = 1000(會報(bào)錯(cuò))
[propName: string]: any, // 給任何名字
greet(): void,
}
// type Person2 = { name: string, ages: number }
let personInfo: PersonInterface = {
name: 'Danile',
ages: 24,
ids: [1, 2, 3] ,
greet() {
console.log('greet')
}
};
// let personInfo: Person2 = {
// name: 'Danile',
// ages: 24,
// };
// console.log(personInfo);
interface 繼承和類的實(shí)現(xiàn)
// interface 繼承
interface PersonInterface {
name: string,
ages: number, // :號摊趾,是必傳
sex?: string, // 币狠?:,是非必傳
readonly salary: number, // 只讀不能修改, personInfo.salary = 1000(會報(bào)錯(cuò))
[propName: string]: any, // 給任何名字
greet(): void,
}
interface StudentInterface {
id: number,
course: string,
}
class People implements PersonInterface, StudentInterface{
name: string = 'Danile';
ages: number = 22;
salary: number = 8000;
id: number = 404;
course: string = 'ts is good';
greet() {
console.log('hello world');
}
}
// **----------------------** //
// interface 的繼承(接口繼承接口)
interface Employee extends PersonInterface {
work: string,
}
const employee: Employee = {
name: 'Danile_001',
ages: 10,
salary: 10000,
work: '前端研發(fā)',
greet() {
console.log('hello kris');
}
}
console.log(employee);
(十)范型的使用,以及場景
// TypeScript 泛型(Generic)
// 在函數(shù)中使用泛型
// function identify<T>(arg: T): T {
// return arg;
// }
// 可以明確指定類型
console.log(identify<string>('string'));
// console.log(identify<string>(25)); // 會報(bào)錯(cuò)
// **----------------------** //
// 交給ts推斷類型
console.log(identify(25));
// 在接口中使用泛型
interface GenericIdentify{
<T>(arg: T): T,
}
function identify<T>(arg: T): T {
return arg;
}
let myIdentify: GenericIdentify = identify;
// 可以明確制定類型
console.log(myIdentify<string>('my-identify'));
// 交給ts推斷類型
console.log(myIdentify(20));
// **----------------------** //
// 為泛型添加約束
function getLength<T extends { length: number }>(obj:T):any {
return obj.length;
}
// 這是給泛型添加約束
// function getLength<T extends number>(obj:T):any {
// return obj.length;
// }
const obj = {
name: 'Danile',
ages: 18,
length: 10,
}
// const obj = 2000000;
console.log(getLength(obj));
// **----------------------** //
// 泛型的應(yīng)用 -- class
class CountNumber<T extends number> {
number1: T;
number2: T;
constructor(num1: T, num2: T) {
this.number1 = num1;
this.number2 = num2;
}
calcalate(): number {
// 前面加上 + 號是ts去運(yùn)算
return +this.number1 * +this.number2;
}
}
const countNumber = new CountNumber<number>(10, 20);
console.log(countNumber.calcalate());
對于范型可能用到的項(xiàng)目中場景
- 登錄用戶名砾层,可能是手機(jī)號(number)总寻,可能是名字(string)。這樣為了可復(fù)用性更高梢为,則可以考慮用范型