- A+
类型
类型 | 例子 | 描述 |
---|---|---|
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接口的实现类 })();