類型 |類型 | 例子| 描述 | | | | | | number | 1,2,-2 | 任意數字 | | string | 'hi',"hi" | 任意字元串 | | boolean | true,false | 布爾值或者true false | | 字面量 | 其本身 | 限制變數的值就是該 ...
類型
類型 | 例子 | 描述 |
---|---|---|
number | 1,2,-2 | 任意數字 |
string | 'hi',"hi" | 任意字元串 |
boolean | true,false | 布爾值或者true false |
字面量 | 其本身 | 限制變數的值就是該字面量的值 |
any | * | 任意類型 |
unknown | * | 類型安全的any |
void | 空值(undefined) | 沒有值或者undefined |
never | 沒有值 | 不能是任意值 |
array | [1,1,2] | 任意js數組 |
object | {name:"孫悟空"} | 任意js對象 |
tuple | [4,5] | 元素,TS新類型,固定長度的數組 |
enum | enum{A,B} | 枚舉,TS新類型 |
介面interface與類型別名type
區別:
介面,只能為對象指定類型
類型別名,不僅可以給對象指定類型,實際上可以為任意類型指定別名
//介面
interface IPerson = {
name: string
age: number
sayHi(): void
}
// 類型別名
type IPerson = {
name: string
age: number
sayHi(): void
}
let person: IPerson = {
name: '劉老師',
age: 18,
sayHi() {}
}
//類型別名
type NumStr = number | string
let numStr: NumStr = 33
let numStr2: NumStr = '66'
字面量類型
思考以下代碼,兩個變數的類型分別是什麼?
let str1 ='Hello Ts'
const str2 : 'Hello Ts'
通過 TS 類型推論機制,可以得到答案:
\1. 變數 str1 的類型為:string。
\2. 變數 str2 的類型為:'Hello TS'。
解釋:
\1. str1 是一個變數(let),它的值可以是任意字元串,所以類型為:string。
\2. str2 是一個常量(const),它的值不能變化只能是 'Hello TS',所以,它的類型為:'Hello TS'。
註意:此處的 'Hello TS',就是一個字面量類型。也就是說某個特定的字元串也可以作為 TS 中的類型。
除字元串外,任意的 JS 字面量(比如,對象、數字等)都可以作為類型使用。
let str1 = 'Hello TS'
const str2: 'Hello TS' = 'Hello TS'
let age: 18 = 18
使用模式:字面量類型配合聯合類型一起使用。
使用場景:用來表示一組明確的可選值列表。
比如,在貪吃蛇游戲中,游戲的方向的可選值只能是上、下、左、右中的任意一個。
解釋:參數 direction 的值只能是 up/down/left/right 中的任意一個。
優勢:相比於 string 類型,使用字面量類型更加精確、嚴謹
function changeDirection(direction: 'up' | 'down' | 'left' | 'right') {}
changeDirection('left')
枚舉類型
//枚舉
/**
*enum
*/
enum Gender {
Male = 0,//可以寫=0 也可以不寫
Female = 1,
}
let i1: { name: string; gender: Gender };
i1 = {
name: "孫悟空",
gender: Gender.Male,
};
console.log(i1.gender === Gender.Female);
// 枚舉:
enum Direction {
Up,
Down,
Left,
Right
}
function changeDirection(direction: Direction) {}
changeDirection(Direction.Left)
枚舉的功能類似於字面量類型+聯合類型組合的功能,也可以表示一組明確的可選值。
枚舉:定義一組命名常量。它描述一個值,該值可以是這些命名常量中的一個。
解釋:
\1. 使用 enum 關鍵字定義枚舉。
\2. 約定枚舉名稱、枚舉中的值以大寫字母開頭。
\3. 枚舉中的多個值之間通過 ,(逗號)分隔。
\4. 定義好枚舉後,直接使用枚舉名稱作為類型註解。
可以直接使用 . 來訪問枚舉中的成員
數字枚舉 字元串枚舉
枚舉是 TS 為數不多的非 JavaScript 類型級擴展(不僅僅是類型)的特性之一。
因為:其他類型僅僅被當做類型,而枚舉不僅用作類型,還提供值 (枚舉成員都是有值的)。
也就是說,其他的類型會在編譯為 JS 代碼時自動移除。但是,枚舉類型會被編譯為 JS 代碼!
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
⬇
var Direction;
(function (Direction) {
Direction["Up"] = "UP";
Direction["Down"] = "DOWN";
Direction["Left"] = "LEFT";
Direction["Right"] = "RIGHT";
})(Direction || (Direction = {}));
說明:枚舉與前面講到的字面量類型+聯合類型組合的功能類似,都用來表示一組明確的可選值列表。
一般情況下,推薦使用字面量類型+聯合類型組合的方式,因為相比枚舉,這種方式更加直觀、簡潔、高效。
any類型
原則上不推薦使用any類型這會讓 TypeScript 變為 “AnyScript”(失去 TS 類型保護的優勢)。
因為當值的類型為 any 時,可以對該值進行任意操作,並且不會有代碼提示
let obj: any = { x: 0 }
// 訪問不存在的屬性 或者 賦值
// obj.aaa
// obj.aaa = 10
// 當作函數調用
// obj()
// 賦值給其他類型的變數
// let n: number = obj
// --
// let a
// a = 1
// a = ''
// a()
// function add(num1, num2) {}
// add(1, 2)
// add(1, '2')
// add(1, false)
解釋:以上操作都不會有任何類型錯誤提示,即使可能存在錯誤!
儘可能的避免使用 any 類型,除非臨時使用 any 來“避免”書寫很長、很複雜的類型!
其他隱式具有 any 類型的情況:1 聲明變數不提供類型也不提供預設值 2 函數參數不加類型。
註意:因為不推薦使用 any,所以,這兩種情況下都應該提供類型!
unknown與any
unknown只霍霍自己 賦值給別人是不行的 而any只要跟他沾邊都會被霍霍都變成any
unknown就是個類型安全的any 賦值給別人前必須做判斷
let e: unknown = 'str';
let s: string;
//unknown 賦值給別人需要先判斷類型
if (typeof e === "string") {
s = e;
}
//或者類型斷言
s = e as string;
s = <string>e;
聯合類型
let a = "male" | "female"
a = "male";
a = "female";
typeof
void never
主要用於函數返回值的確定
function fn(num): void {
if (num>0) {
return; //不應有返回值
} else {
return undefined;
}
}
function fn2(): never{
throw new Error("出錯了");//就是執行不完 一定會出錯
}
object 與函數結構的類型聲明
一般是不用object 限制太寬泛了
let a1: object;
a1 = {}
a1 = function () {
};
//用於指定哪些屬性
let b1: { name: string, age?: number }//加上問好表示屬性可選
b1 = { name: '孫悟空' }
let c1: { name: string, [propName: string]: any }// [propName: string]: any表示可以有任意類型的屬性
c1 = { name: "豬八戒", age: 18, gender: "male" }
//定義函數的參數結構 的類型聲明
let d1: (a: number, b: number) => number;
d1 = function (a: number, b: number) { return a + b; };
array
//數組
let e1: string[];//表示字元串數組
e = ['a', 'b', 'c', 'd'];
let f1: number[];
let g1: Array<number>;
元組
長度固定的數組
//元組
/**
* 語法: [類型,類型,類型]
*/
let h1: [string, string, number];
h1 = ["hello", "world", 123];
或 | 和 與&
// | 表示或 & 表示且
let j1: { name: string } & { age: number }
j1={name: "孫悟空",age:18}
編譯選項
tsc app.ts -w
-w watch模式 只監視app.ts
使用tsc 編譯此項目下的全部ts文件 前提是得由tsconfig.json文件 即使這個文件為空json也會按照預設的選項來編譯!
webpack打包TS
package.json 需要哪些包
{
"name": "ts-webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"start": "webpack serve --open "
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.18.13",
"@babel/preset-env": "^7.18.10",
"babel-loader": "^8.2.5",
"clean-webpack-plugin": "^4.0.0",
"core-js": "^3.24.1",
"html-webpack-plugin": "^5.5.0",
"ts-loader": "^9.3.1",
"typescript": "^4.7.4",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.10.0"
}
}
webpack.config.js 定義打包的方式
//引入路徑包
const path = require("path");
//引入一個插件包
const HTMLWebpackPlugin = require("html-webpack-plugin");
//引入clean插件
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
//添加模式
mode: "development",
//指定入口文件
entry: "./src/index.ts",
//指定打包文件所在目錄
output: {
//指定目錄
path: path.resolve(__dirname, "dist"),
//指定名字
filename: "index.js",
//配置環境相容問題
environment: {
arrowFunction:false
}
},
//指定打包時要用到的模塊
module: {
rules: [
{
//test指定的是規則生效的文件
test: /\.ts$/, //匹配以ts結尾的文件
//use 要使用的loader 數組中的內容從後往前執行
use: [
{
//設置載入器
loader: "babel-loader",
//設置babel
options: {
//設置預定義的環境
presets: [
[
//指定環境的插件
"@babel/preset-env",
//配置信息
{
//要相容的預設瀏覽器
targets: {
//瀏覽器版本
"chrome": "58",
"ie":"11"
},
//指定corejs的版本
"corejs": "3",
//使用corejs的方式
"useBuiltIns": "usage"
}
]
]
},
},
"ts-loader",
],
//exclude 排除哪些文件
exclude: /node-modules/,
},
],
},
//配置webpack的插件
plugins: [
new CleanWebpackPlugin(),
new HTMLWebpackPlugin({
// title: "這是一個自定義的title",
template: "./src/index.html",
}),
],
//用來設置引用模塊
resolve: {
extensions: [".ts", ".js"],
},
};
tsconfig.json ts的編譯方式
{
"compilerOptions": {
"module": "ES2015",
"target": "ES2015",
"strict": true
}
}
類
//使用class關鍵字來定義一個類
/**
* 屬性
* 方法
*/
class Person{
//實例屬性
name: string = "孫悟空";
//在屬性前加關鍵字static可以定義類屬性(靜態屬性)即不需要創建實例就可訪問的屬性
static age: number = 18;
//只讀屬性
readonly gender: string = "male";
//實例方法
say() {
console.log('hello')
}
//靜態方法
static run() {
console.log('run')
}
}
const per = new Person();
console.log('per.name', per.name);
console.log('per', per);
per.say();
console.log('Person.age', Person.age)
Person.run();
構造函數與this
class Dog{
name: string;
age: number;
//構造函數
constructor(name: string, age: number) {
console.log('構造函數執行了',this)
this.name= name
this.age= age
}
bark() {
console.log('wangwangwang')
}
}
const dog = new Dog('xiaohei',4);
console.log('dog',dog)
類的繼承
(() => {
class Animal {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`${this.name}sayHello`);
}
}
class Dog extends Animal {
sayHello(): void {
console.log("wangwangwang"); //子類覆蓋掉父類的方法的形式叫重寫
}
}
class Cat extends Animal {
run() {
console.log(`${this.name}run`);
}
}
const dog = new Dog("旺財", 5);
const cat = new Cat("咪咪", 3);
console.log("cat", cat);
console.log("dog", dog);
dog.sayHello();
cat.sayHello();
cat.run();
})();
super
(() => {
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello() {
console.log(`${this.name}sayHello`);
}
}
class Dog extends Animal {
age: number;
constructor(name: string, age: number) {
super(name);//必須先調用父類的構造函數
age = this.age;
}
sayHello(): void {
super.sayHello(); //super表示當前類的父類
}
}
const dog = new Dog('旺財',1);
dog.sayHello();
})();
抽象類
(() => {
abstract class Animal {
//我們不希望直接用Animal來創建實例,我們只需要讓它是一個超類 用來被繼承
//所以我們給它加上abstract 那麼這個類就只能被繼承 不能用它創建實例
//抽象類可以添加抽象方法
name: string;
constructor(name: string) {
this.name = name;
}
//抽象方法只能在抽象類里定義 抽象方法沒有方法體
//子類必須對抽象方法進行重寫
abstract sayHello(): void;
}
class Dog extends Animal {
//非抽象類繼承抽象類 必須重寫父類中的抽象方法
sayHello(): void {
console.log("汪汪汪");
}
}
const dog = new Dog("旺財");
dog.sayHello();
})();
介面
(() => {
/**
* 對於類型聲明(類型別名) 只能聲明一次
*
* 對於介面 可以重覆聲明 多次聲明那麼創建的實例應把介面中的定義的都實現
* 介面可以限制類的結構
* 介面只定義類的結構不定義類的實際值
* 介面的所有方法都是抽象方法
*
*
*
*/
//描述一個對象的類型 類型別名
type myType = {
name: string;
age: number;
};
let obj: myType = {
name: "sss",
age: 1,
};
/**
* 介面用來定義一個對象的結構
* 用來定義一個類中應該包含哪些屬性和方法
*/
interface myInterface {
name: string;
age: number;
}
interface myInterface {
gender: string;
sayHello(): void;
}
let obj1: myInterface = {//當作類型使用
name: "aaa",
age: 2,
gender: "male",
sayHello() {
console.log("hello");
},
};
//實現介面
class Myclass implements myInterface {
name = '孫悟空';
age = 18;
gender = 'male';
sayHello() {
console.log("嘿嘿")
}
}
})();
屬性的封裝
private getter setter
(() => {
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
let per = new Person("孫悟空", 18);
console.log("per", per);
per.name = "豬八戒";
per.age = -18;
console.log("per", per); //可以任意修改 年齡能有負數嘛?
/**
* 上面的屬性是在對象中設置的,屬性可以任意的被修改
* 屬性可以任意被修改將會導致對象中的數據會變得非常不安全
*/
class Animal {
/**q
* 可以在屬性前添加修飾符
*/
public name: string; //公共屬性可以在任意位置(訪問)更改 預設就是public
private age: number; //私有屬性 只能在類內部(訪問)修改
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
//getter 添加方法讓私有屬性可以在外部訪問
getAge() {
return this.age;
}
//setter 設置屬性
setAge(value: number) {
if (value > 0) {
this.age = value;
}
}
}
let dog = new Animal("旺財", 2);
console.log("dog", dog);
console.log("dog.getAge()", dog.getAge());
dog.setAge(-8);
console.log("dog", dog); //改不了
})();
protected 與 constructor 語法糖
(() => {
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
let per = new Person("孫悟空", 18);
console.log("per", per);
per.name = "豬八戒";
per.age = -18;
console.log("per", per); //可以任意修改 年齡能有負數嘛?
/**
* 上面的屬性是在對象中設置的,屬性可以任意的被修改
* 屬性可以任意被修改將會導致對象中的數據會變得非常不安全
*/
class Animal {
/**q
* 可以在屬性前添加修飾符
*/
public _name: string; //公共屬性可以在任意位置 包括子類 (訪問)更改 預設就是public
private _age: number; //私有屬性 只能在類內部(訪問)修改
//protected 受保護的屬性,只能在當前類和當前類的子類中訪問
constructor(name: string, age: number) {
this._name = name;
this._age = age;
}
// //getter 添加方法讓私有屬性可以在外部訪問
// getAge() {
// return this.age;
// }
// //setter 設置屬性
// setAge(value: number) {
// if (value > 0) {
// this.age = value;
// }
// }
//ts中設置獲取屬性 設置屬性 的方法 不會改變訪問數據的方法
/**
* 不是特別複雜的修改時 一般用不到
*/
get age() {
return this._age;
}
set age(value: number) {
if (value > 0) {
this._age = value;
}
}
}
let dog = new Animal("旺財", 2);
// console.log("dog", dog);
// console.log("dog.getAge()", dog.getAge());
// dog.setAge(-8);
console.log("dog.age", dog.age);
// console.log("dog", dog); //改不了
dog.age = 8;
console.log("dog", dog);
/**
* 語法糖
*/
class C {
//得用public
constructor(public name: string, public age: number) {}
}
let c = new C("666", 6);
console.log("c", c);
})();
泛型
(() => {
function fn(a: any): any {
return a;
}
/**
* 在定義函數或是類時遇到類型不明確的 可以使用泛型
*
*/
function fn1<T>(a: T): T {
//我不知道a到底是是麽類型但是我知道 返回值和入參的類型時相同的
return a;
}
fn1(10); //自動推斷
fn1<string>("test"); //類型斷言
function fn2<T, K>(a: T, b: K): T {
console.log("b", b);
return a;
}
fn2(123, "test");
fn2<number, string>(333, "test");
interface Test {
length: number;
}
//泛型T得是Test的實現類(子類)
function fn3<T extends Test>(a: T): number {
return a.length;
}
fn3([1, 2, 2]); //傳入的參數的類型得是Test介面的實現類
})();
倉庫地址:https://gitee.com/bbigger004/tslearn.git