TypeScript学习笔记
最大的特点: 类型声明(type)
把js变成静态类型的语言
从技术上讲TypeScript就是具有静态类型的 JavaScript 。
静态类型更有利于构建大型应用
静态类型在编译时就能发现类型上的错误
项目越大、越复杂,优势越明显。
和javascript的区别:
1.这使开发人员能够快速检测错误并调试应用程序
2.js的超集,所有的js代码都可以在ts中使用
3.TypeScript 通过类型注解提供编译时的静态类型检查。
4.TypeScript 中的数据要求带有明确的类型,JavaScript不要求。
5.TypeScript 中引入了模块的概念,可以把声明、数据、函数和类封装在模块中。
类型
例子 | 描述 | |
---|---|---|
number | 1, -33, 2.5 | 任意数字 |
string | 'hi', "hi", hi |
任意字符串 |
boolean | true、false | 布尔值true或false |
字面量 | 其本身 | 限制变量的值就是该字面量的值 |
any | * | 任意类型 |
unknown | * | 类型安全的any |
void | 空值(undefined) | 没有值(或undefined) |
never | 没有值 | 不能是任何值 |
object | {name:'孙悟空'} | 任意的JS对象 |
array | [1,2,3] | 任意JS数组 |
tuple | [4,5] | 元组,TS新增类型,固定长度数组 |
enum | enum{A, B} |
1. 安装
cnpm install -g typescript
//or
yarn global add typescript
2.检查版本
tsc -v
常用编译选项
--noEmitOnError 报错时不生成输出文件
严格模式
开启以下默认选项
{ "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, "strictPropertyInitialization": true, "noImplicitThis": true, "alwaysStrict": true }
ts代码有错误,但是符合js规范的,依然可以编译,为了推广。
js中的函数是不考虑参数的类型和个数的
function sum(a,b) { return a + b ; } console.log('让我康康sum1',sum(123,'456')) // '123456' 没有提示,也没有报错,一不小心就算成了一个字符串,这个结果如果用到别的地方,可能导致一连串的错误,而且这个错误不好找。
ts 类型声明 可以用在变量、参数和返回值上
function tsSum(a:number,b:number): number {// 函数返回值的类型 return a + b; }
声明多个类型
let d: boolean | string; // 可以使用|来连接多个类型(联合类型)
关闭TS的类型检测(和JS一样了,不建议使用)
let f: any; // 显式any, 关闭TS的类型检测
let f; // 隐式any,一样关闭了TS的类型检测
x = f // f赋值给x后,会关闭x的类型检测
unknown
let e: unknown; // unknown 实际上是一个类型安全的any // unknown 类型的变量,不能直接赋值给其他变量 要先判断类型 if( typeof e === 'string'){ d = e }
// 类型断言, 可以用来告诉解析器变量的实际类型
d = e as boolean; //语法1
d = <boolean>e; //语法2
void
// void 用来表示空,以函数为例,就表示没有返回值的函数。 function fn(): void { }
never
// 永远不会返回结果,用来检查报错的 function fn2(): never { throw new Error("报错了!"); }
object 限制对象不是为了限制他是不是一个对象,而是限制对象中的属性。
// 这样写就只能传一个类型为string的参数 let g: {name:string}; g = {name:'小子'};
// 属性可写可不写 let h: {name:string,age?:number}
// 可以传任意个数,任意类型的参数 let i: {name:string,[propName: string]:any} i = {name:'小子',age:20,sex:'male'}
设置函数结构的类型声明
// 规定函数的传参类型以及返回类型 let sum2: (a: number, b: number) => number;
array
// 定义字符串数组 let j: string[];
tuple 元组,就是固定长度的数组
let k: [string,number];
enum 枚举
enum Gender { male = 0, female = 1 } console.log('让我康康male的值',Gender.male) // 0
&两个条件都要满足
let m: {name:string} & {age:number}
类型的别名
// 类型的别名 type myType = 1 | 2 | 3 | string | boolean; let n: myType; // n 为1 2 3 字符串 布尔值都可以
编译
ts编译器 tsconfig.json( 直接在终端输入 tsc即可)
{ // include 用来指定哪些ts文件需要被编译 "include": [ // 一个*是任意文件,两个*是任意目录 "./src/**/*" ], // 排除掉不想被编译的文件,默认值 ["node_modules","bower_components","jspm_packages"] "exclude": ["./src/abc/*"], // 指定被编译文件的列表,只有需要编译的文件少时才会用到 "files": ["src/code.ts", "src/abc/code.ts"], // 编译器的选项 "compilerOptions": { // 指定TS被编译的ES版本 "target": "ES2015", // 指定要使用的模块化规范 "module": "AMD", // lib 用来指定项目中要使用的库,一般是在浏览器环境下使用的话,不需要去修改lib配置 "lib": ["DOM", "ES2015"], // 指定编译后的文件所在的目录 "outDir": "./dist", // 合并编译后的文件,设置outFile后,所有的全局作用域中的代码会合并到同一个文件中(很少用) "outFile": "./dist/merge.js", // 是否对js文件进行编译 "allowJs": false, // 检查js代码是否符合语法规范 "checkJs": false, // 是否去除注释 "removeComments": true, // 不生成编译后的文件,一般是用来检查语法 "noEmit": false, // 有错误时不生成编译后的文件 "noEmitOnError": true, // 所有严格检查总开关 建议打开 "strict": true, // 设置编译后的文件是否开启严格模式 "alwaysStrict": false, // 不允许隐式any类型 "noImplicitAny": true, // 不允许不明确类型的this "noImplicitThis": true, // 严格的检查空值 "strictNullChecks": false } }
ts和webpack结合
需要依赖
webpack webpack-cli typescript ts-loader html-webpack-plugin webpack-dev-server
webpack.config.js配置
// 引入一个包 const path = require('path'); // 引入html插件 const HTMLWebpackPlugin = require('html-webpack-plugin'); // 引入clean插件 const { CleanWebpackPlugin } = require('clean-webpack-plugin'); // webpack中的所有的配置信息都应该写在module.exports中 module.exports = { // 指定入口文件 entry: "./src/index.ts", // 指定打包文件所在目录 output: { // 指定打包文件的目录 path: path.resolve(__dirname, 'dist'), // 打包后文件的文件 filename: "bundle.js", // 告诉webpack不使用箭头 environment:{ arrowFunction: false } }, // 指定webpack打包时要使用模块 module: { // 指定要加载的规则 rules: [ { // test指定的是规则生效的文件 test: /\.ts$/, // 要使用的loader use: [ // 配置babel { // 指定加载器 loader:"babel-loader", // 设置babel options: { // 设置预定义的环境 presets:[ [ // 指定环境的插件 "@babel/preset-env", // 配置信息 { // 要兼容的目标浏览器 targets:{ "chrome":"58", "ie":"11" }, // 指定corejs的版本 "corejs":"3", // 使用corejs的方式 "usage" 表示按需加载 "useBuiltIns":"usage" } ] ] } }, 'ts-loader' ], // 要排除的文件 exclude: /node-modules/ } ] }, // 配置Webpack插件 plugins: [ new CleanWebpackPlugin(), new HTMLWebpackPlugin({ // title: "这是一个自定义的title" template: "./src/index.html" }), ], // 用来设置引用模块 可以解决import {m} from 'm.ts'; 无法打包问题 resolve: { extensions: ['.ts', '.js'] } };
打包后
还是有一些浏览器无法识别,所以要引用插件
@babel/core @babel/preset-env babel-loader core-js
引入less
less less-loader
引入postcss
postcss postcss-loader postcss-preset-env
处理css
css-loader style-loader
1.Autoprefixer 自动获取浏览器的流行度和能够支持的属性,并根据这些数据帮你自动为 CSS 规则添加前缀
2.CSS MODULES 能让你永远不用担心命名太大众化而造成冲突,只要用最有意义的名字就行了。
Class 类
使用class关键字来定义一个类,主要包含两个部分:属性和方法。
class Person{ name:string = '亚瑟'; age:number = 18; // 属性前使用static关键字可以定义类属性(静态属性) static gender:string = 'male' } // per就是实例 const per = new Person() // per.age 就是实例属性 // 类属性或者叫静态属性 // Person.age // 不存在 Person.gender // 存在
可以自定义的类
class Dog { name:string; age:number; // constructor 被称为构造函数 构造函数会在对象创建时调用 constructor(name:string,age:number) { // 在实例方法中,this就表示当前的实例, 谁调用它,this就是谁 比如 const dog1 = new Dog('小黑',3) this.name就是小黑 // 在构造函数中,当前对象就是当前新建的那个对象 // 可以通过this向新建的对象中添加属性 this.name = name; this.age = age; } bark(){ alert('汪汪汪') } } const dog = new Dog('花花',3)
类的继承
class Animal { name:string; age:number; // constructor 被称为构造函数 构造函数会在对象创建时调用 constructor(name:string,age:number) { // 在实例方法中,this就表示当前的实例, 谁调用它,this就是谁 比如 const dog1 = new Dog('小黑',3) this.name就是小黑 // 在构造函数中,当前对象就是当前新建的那个对象 // 可以通过this向新建的对象中添加属性 // 子类中的方法,可以覆盖父类中的方法 this.name = name; this.age = age; } sayHello(){ alert('未知动物') } }
class Cat extends Animal{ sayHello(){ alert('我是'+ this.name) } }; const cat = new Cat('小花',3) cat.sayHello()
在类的方法中,super表示当前类的父类
子类(派生类)中,对要添加属性,就要对属性进行初始化表达式,并且在构造函数中赋值
子类如果要使用构造函数,那么要先调用父类的构造函数,不然会覆盖父类的构造函数(报错)
class Bird extends Animal { gender: string; // 新属性 constructor(name:string,age:number,gender:string){ super(name,age); this.gender = gender; } }
抽象类 以abstract开头的类
// 抽象类和其他类区别不大,只是不能用来创建对象 // 抽象类就是专门用来被继承的类 abstract class fruits { name:string; constructor(name:string){ this.name = name } // 抽象方法使用abstract开头,没有方法体 // 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写 abstract sayHello():void; }
接口 用interface开头
和类型声明的区别: 1.接口可以在定义类的时候去限制类的结构 2.类型声明不能重复声明同一个 ,而接口可以重复,最后的结果就是把所有重复的里面的属性和方法加在一起
// 接口用来定义一个类结构,和一个类中应该包含哪些属性和方法 // 同时接口也可以当成类型声明去使用
// 接口中所有的属性都不能有实际的值,只定义对象的结构
// 所有方法都是抽象方法
interface myInterface { name: string; age: number; } const obj: myInterface = { name:'小明', age:18 } // 类型声明 type myType = { name: string; age: number; };
和抽象类的区别: 1.抽象类可以定义抽象方法,也可以定义普通方法。接口只能有抽象方法 2. 抽象类用extens继承,接口用implement继承。
类属性的封装
class Person { // public 修饰的属性可以在任意位置访问(修改)默认值 public _name: string; // 私有属性,只能在类内部进行修改 private _age: number; constructor(name: string, age: number) { this._name = name; this._age = age; } // 定义方法,用来获取name属性 getName(){ return this._name; } // 定义方法,用来设置name属性 setName(value: string){ // 提高代码容错率 可以在里面进行判断 if(value.length > 1){ this._name = value; } } } const per = new Person('诸葛亮', 18); per._name = '周公瑾'; per._age = 20; // 私有属性 无法访问
直接将属性定义在构造函数中
class C { // 可以直接将属性定义在构造函数中 constructor(public name:string, public age:number){ } } const c = new C('c',3);
类型断言
解决方法:
this.element = document.getElementById('snake')!;
加一个! 是not null 的断言操作符,不执行运行时检查,告诉编译器只需要知道这个东西
2.第二个问题
报错
解决方法:
this.head = document.querySelector('#snake > div') as HTMLElement;
insertAdjacentText方法与 insertAdjacentHTML方法
insertAdjacentHTML(param1,param2)
第一个参数
1. beforeBegin: 插入到标签开始前
2. afterBegin:插入到标签开始标记之后
3. beforeEnd:插入到标签结束标记前
4. afterEnd:插入到标签结束标记后
<!--beforebegin
--><p>
<!--afterbegin
--> foo <!--beforeend
--></p>
<!--afterend
-->
第二个参数 : HTMLElement
与innerHTML的区别:
innerHTML是替换容器内的元素,而这个是往特定位置去添加元素
insertAdjacentText(param1,param2)
第一个参数一样,第二个参数是字符串
开始
1.init json文件
tsc --init
把 "outDir": "./", 打开 , 改为 "outDir": "./js",
2.监听ts
vscode 终端 => 运行任务
3.数据类型
布尔类型 boolean
let flag:boolean = true;
let num:number = 666;
let str:string = 'This is TypeScript';
let arr:number[] = [1,2,3];let arr2:object[] = [ {a:10}, {a:10}];
let arr3:Array<number> = [1,2,3];let arr4:Array<object> = [{a:1},{a:2}];
let arr33:any[] = ['123',true,66]console.log('arr33',arr33)
let arr5:[string,number,object,boolean] = ['123',3.14,{},false];
enum Color {red,blue,orange};let r:Color = Color.red; //0
function run():void {console.log('66')}run();
function crnumber(type:number) {console.log('type',type)return type}crnumber(123)
var a:never;a = (()=>{// 抛出一个错误throw new Error("错误");})()
function fn1():number {return 123}
var fn2 = function ():string {return 'es50'}alert(fn2());
function select(params:string,params2?:number) {console.log('配置可选参数',params,params2)}select('arg1')
function sum(...array:number[]):number {let sum = 0;for (let index = 0; index < array.length; index++) {console.log('array[index]',array[index])sum += array[index]}return sum}let asr = [1,2,3,4]alert(sum(...asr))
function done(number:number):number;function done(string:string):string;function done(params:any):any {if (typeof params === 'string') {return '我叫' + params;}else if(typeof params === 'number'){return '年龄' + params;}}alert(done('张三'))
function Person() {this.name = '张三';this.age = 20;this.run = function () {alert(this.name + '在运动')}}
Person.getInfo = function(name){alert(name + '是静态方法')}Person.prototype.sex = '男';
Person.prototype.work = function(){alert(this.name + '在工作');}var p = new Person()p.run();p.work();console.log('p',p.sex)Person.getInfo('小李子')
function Dog() {Person.call(this) // 对象冒充实现继承}var d = new Dog();
<script> // ES5中的类, 可以在构造函数/原型链中 添加属性/方法; function Person(name,age) { this.name = name; this.age = age; this.run = function () { alert(this.name + '在运动') } } // 静态方法 Person.getInfo = function(name){ alert(name + '是静态方法') } Person.prototype.sex = '男'; // 实例方法 Person.prototype.work = function(){ alert(this.name + '在工作'); } // Dog类 继承Person类 function Dog(name,age) { Person.call(this,name,age) // 对象冒充实现继承 实例化子类可以给父类传参 } //原型链继承 Dog.prototype = new Person(); var d = new Dog('花花',1); // d.work() // 对象冒充不能继承原型链方法和属性 Uncaught TypeError: d.work is not a function d.run(); d.work() </script>
var Animal = /** @class */ (function () { function Animal(t) { this.type = t; } Animal.prototype.run = function () { alert(this.type + '动物'); }; return Animal; }()); var a = new Animal('哺乳'); a.run();