随手一提
javaScript 脚本语言 借助宿主环境运行 网页浏览器 / node环境
typeScript基础:
原始数据类型
任意值类型:
如果是 any 类型,则允许被赋值为任意类型
类型推论:
let myFavoriteNumber;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
如果定义时没有赋值 不管之后有没有赋值 都会被推断成any类型 完全不被类型检查
联合类型:
- 联合类型用 | 分隔 let myFavoriteNumber:string | number
- 当不确定联合类型的变量 是哪个类型的时候 只能访问联合类型的共有属性或者方法
对象类型——接口
- 例子:
interface Person {
name:string;
age:number;
}
let tom:Person = {name:'asad', age:23}
赋值的时候 变量的形状必须和接口的形状保持一致(初始整体赋值时) 变量比接口少一些或多一些属性不被允许
可选属性 不完全匹配时
inteface Person {
name:string;
age?:number
}
- 任意属性
```js
interface Person {
name:string;
age?:number,
[propName:string]:any
}
interface Person {
name:string;
age?:number,
[propName:string]:string
}
let tom:Person = {
name:'tom',
age:24,
gender:'male'
}
//任意属性的值允许是string 但是可选属性 age:number 不是string 的子属性
```
一旦定义了任意属性 确定属性和可选属性的类型必须是它的类型的子继
一个接口中只能定义一个任意属性 接口中有多个类型的属性 可以在任意属性中使用联合类型
```js
interface Person {
name:string;
age?:number;
[propName:string]:string|number
}
```
- 只读属性 readonly
```js
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
id: 89757,
name: 'Tom',
gender: 'male'
};
tom= {
//id:1212, //注销 报错 !! Property 'id' is missing in type '{ name: string; }' but required in type 'Person'.
name:'2ee',
// gender:'male'
}
tom.id = 2112 //报错!! Cannot assign to 'id' because it is a read-only property.
```
属性id 是只读属性 在整体赋值时 必须带上 第一次整体赋值 还是后整体赋值
属性id 单独赋值时 报错
数组的类型
- 例子
let workers :number[] = [1,2,3,1,3]
- 数组的泛型
let powers :Array<number> = [3,4,5,1,4]
- 用接口表示数组
- 类数组
类数组不是数组类型 不能用普通数组的方式描述 而应该用接口
类数组都有自己的接口定义 如 IArguments, NodeList, HTMLCollectionlet args:{ [index :number]:number; length:number; callee:Function; }= arguments;
function sun () { let args:IArguments = arguments; } // IArguments 是TypeScript 定义好的类型 interface IArguments { [index : number]:any; length:number; callee:Function; }
- 数组 any
any 数组中允许出现任意类型:let list: any[] = [{class:'dqw'},'21',1]
- 数组 any
函数的类型
-
函数声明2种形式
函数声明 function Declaration
函数表达式 function Expressionfunction sum(x,y){ return x+y; } let mus = function(x,y){ return x+y; } function sum(x:number,y:number):number{ return x+y }
-
函数表达式
let mus:(x:nummber,y:number)=>number= function(x:number,y:number){ return x+y; }
接口定义函数的形状 有点复杂&%&#
-
函数可选参数
可选参数后面不允许再出现必需参数function buildName(firstName?:string,lastName:string){ if(firstName)... else return ... } let tom = buildName(undefined,'tom')
-
参数默认值
默认值的参数 被视为可选参数 此时这个可选参数位置不受限制function buildName (firstName:string = 'Tom' , lastName : string){ return firstName + '' +lastName; } let cat = buildName(undefined,'Cat');
-
剩余参数(rest参数) 是数组类型
function push(array:any[], ...items:any[]){}
重载
类型断言
- type Assertion : 手动指定一个值的类型
- 语法 2 种形式
值 as 类型
<类型>值
-
断言用法
1. 联合类型断言为其中一个类型 interface Fish { name:string; swim():void; } interface Cat { name:string; run():vold; } function isCat(animal:Cat | Fish ):boolean{ if(typeof (animal as Cat).run==='Function'){ return true; }else{ return false; } } 2. 父类型断言为子类型 class ApiError extends Error { code :number =0; } class HttpError extends Error { status :number = 200; } function isApierror (error :Error ){ if(typeof (error as ApiError).code === 'number'){ return true; } return false; } 等价于 error instanceof ApiError 当 ApiError 和 HttpError 是接口时 不能使用 instanceof, 接口是一个类型 ts编译过后直接删除 类型, js运行时没有类型 则使用instanceof会报错 3. 将任何一个类型断言为 any 有时候非常确定 一段代码不会报错 window.foo = 3; // ts编译时 会报错 提示 window上没有这个属性 (window as any).foot = 3; -在any上访问任何属性都是允许的 -TypeScript设计理念之一:我们需要在类型严格性和开发便利性之间掌握平衡 4. 将any 断言为一个具体类型
-
类型断言的限制
- 要使得A被断言成B,只需要A兼容B或B兼容A即可
- 若A兼容B,那么A能够被断言成B,B也能够被断言成A
-
双重断言
1. 任何类型都能被断言为any 2. any可以被断言成任何类型 interface Cat { run():void; } interface Fish{ swim():void; } function test (cat :Cat){ return (cat as any as Fish); //报错 Cat 与 Fish 互不兼容 } 3. 违背限制,触发迫不得已,千万别用双重断言
-
类型断言 vs 类型转换
类型转换还是要调用类型转换的方法
类型断言只会影响ts编译是的类型,在编译结果中会被删除:function toBoolean(something :any):boolean { return something as boolean; } toBoolean(1);//返回1 function toBoolean (something:any):boolean{ return Boolean(something); }
-
类型断言 vs 类型声明
- animal 断言为 Cat, 只需要满足Animal兼容Cat 或 Cat兼容Animal即可
- animal 赋值给 tom(Cat类型),要满足 Cat兼容 Animal
- Cat不兼容Animal 不能将父类的实例赋值给类型为子类的变量
- "兼容" 换成 "包括" 更容易理解
- 子类不能包括父类
泛型
- 定义:Generics 是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候在指定类型的一种特性
// 泛型类
class KeyValuePair<T> {
constructor(public key: T, public value: string) { }
}
let pair = new KeyValuePair<string>('1', 'w');
//通用类 K V
class KeyValue<K, V>{
constructor(public key: K, public value: V) { }
}
let pair1 = new KeyValue<string, string>('1', '6')
//泛型函数
function warpInArray(value: number) {
return [value];
}
let numbers = warpInArray(1);// 返回数字类型数组
//通用
function warpInArray1<T>(value: T) {
return [value];
}
class ArrayUntils {
static warpInArray1<T>(value: T) {
return [value];
}
}
let numbers1 = ArrayUntils.warpInArray1(1);
//泛型接口
//http://mywebsite.com/users
//http://mywebsite.com/products
interface Result<T> {
data: T | null,
error: string | null
}
function fetch<T>(url: string): Result<T> {
return { data: null, error: null };
}
interface User {
username: string;
}
interface Product {
title: string;
}
let result = fetch<User>('url');
result.data.username
let res1 = fetch<Product>('url')
res1.data.title
//泛型约束
function echo<T>(value: T): T {
return value;
}
echo('1'); //类型推断 echo<string>('1')
function echo1<T extends number | string>(value: T): T {
return value;
}
echo1('1'); //只能传递 number 或者 string 类型参数
// 只能传递这个形状的对象
function echo2<T extends { name: string }>(value: T): T {
return value;
}
echo2({ name: 'a' })
interface Person {
name: string;
}
function echo3<T extends Person>(value: T): T {
return value;
}
//可以用 calss 约束
class Person {
constructor(public name: string) { }
}
class Custumer extends Person {
}
echo(new Person('a'))
echo(new Custumer('b')) // 实例间接的来源于 Person
// 扩展泛型类
// 泛型和继承
interface Product {
name: string;
price: string;
}
class Store<T> {
protected _objects: T[] = [];
add(obj: T): void {
this._objects.push(obj);
}
}
// let store = new Store<Product>();
// store.objects=[];
//在 CompressibleStore 后添加 <T> 编译器知道了 store 后的 <T>的来源
// 传递泛型类型的参数 Pass on the generic type parameter
// 1. 基类中的泛型类型参数 也可被使用在 类中
class CompressibleStore<T> extends Store<T>{
compress() { }
}
let store = new CompressibleStore<Product>()
// store 有2个方法 compress add
// class SearchableStore<T> extends Store<T> {
// find(name: string): T | undefined {
// return this._objects.find(obj => obj.name === name); //此处报错 Propert 'name' does not exist on type 'T' 编译器不知道T 有name属性
// }
// }
// T 泛型约束 可以使用具有name 属性的任何类型的对象
// 2. 限制泛型类型参数 Restrict the genertic type parameter
class SearchableStore<T extends { name: string }> extends Store<T>{ }
//3. Fix the generic type parameter
//具体的类型 不需要泛型
class ProductStore extends Store<Product> {
filterByCategory(category: string): Product[] {
return [];
}
}
//泛型类型有默认值
声明文件
- *.d.ts文件
- 全局的声明 局部模块声明
- 全局声明 :declare关键字 配合 tsconfig.json中 include 、 exclude、 file 默认加载
- 局部模块: 含有export/import关键字 模块内
- npm 包类型声明:
- axios 包 引入 axios时,假设没有对应的类型声明文件,引入会报错
- 配合 tsconfig.json 中的path 即export function createInstance(): AxiosInstance 引入axios自动加载 path中:[ "axios": types/axios.d.ts] 中ts文件
- export interface XXX 结合declare使用 此时 declare不会声明一个全局变量
- npm 包扩展:
- 如果是需要扩展原有模块的话,需要在类型声明文件中先引用原有模块,再使用 declare module 扩展原有模块
- npm 包类型声明:
- 例子
// String 扩展一个方法 interface String { hello:()=>any; } "a".hello();
前端模块化
- CommonJs module.exports require 同步加载
- AMD define require 异步加载、依赖前置
- CMD define seajs(requirejs) 异步加载、依赖就近
- UMD 结合 AMD + CommonJs
- ESM(ES6 Module) export import
服务端: CommonJs 、 UMD(AMD + CommonJS)
浏览器: AMD、 CMD、 ESM
-
Commonjs
//a.js module.exports = { a: function (msg){console.log(msg)} } // b.js引用 const a = require ('./a.js') a.a('sc')
-
AMD
- AMD在浏览器端异步加载,AMD推崇依赖前置,加载完模块之后就会立即执行它
- 例子
//https://blog.csdn.net/weixin_38407447/article/details/117407660 AMD 异步加载机制 // amd.js 类似于 require.js 建立requir函数 define函数 require.config 函数等 function define(...arr){} function require(deps,acllback){ return new Promise(saync (resolve ,reject)=>{}).then(e =>{return callback(...e)}) } // a.js模块 依赖b模块 此处b模块不写 define('a',['b'],function(b){ return {run : function(){return 'ssa'}} })
//AMD用法 amd.html <html> <head> <meta charset="UTF-8"> <script src= './amd.js'></script> //类似引用require.js </head> <body> </body> <script> require.config({ path:{ 'lodash':"https://cdn.XXXX/XXX/lodash.js" } }) require(['lodash','a'],function(lodash,a){ ...}) </script> </html>
- CMD
- CMD在浏览器端异步加载,CMD推崇依赖就近,加载完模块不会立即执行,只是加载,等到需要的时候才会执行
- 例子
define(function(require,exports,module){ var $ = require('jquery.js') //按需执行 var add = function(a,b){return a+b} }) exports.add = add;
内置对象
类别别名
字符串字面量类型
类与接口
- 类实现接口 可以实现多个接口 类只能继承一个类
- 接口继承接口 多个
- 接口继承类 TypeScript中可以
代码检查
TypeScript 自带 ts检测 \ 还要使用ESlint
eslint 能够发现出一些 tsc 不会关心的错误,检查出一些潜在的问题,所以代码检查还是非常重要的
-
TypeScript中使用ESlint
- 安装 npm install --save-dev eslint
- 安装 ESlint 解析 ts解析器 npm install --save-dev typescript @typescript-eslint/parser
- 安装 额外的适用于 ts 语法的规则 npm install --save-dev @typescript-eslint/eslint-plugin
- 创建配置文件对哪些规则进行检查 .eslintrc.js 或者 .eslintrc.json
-
检查一个ts文件
- 执行 ./node_modules/.bin/eslint index.ts (不是全局eslint脚本)
- 检查整个项目的ts文件
{ "scripts": { "eslint": "eslint src --ext .ts" } }
-
VScode集成ESlint检查
- 编辑器中集成eslint检查,可以在开发过程中发现错误,甚至保存后自动修复
- 安装eslint 插件
- VSCode 中的 ESLint 插件默认是不会检查 .ts 后缀的,需要在「文件 => 首选项 => 设置 => 工作区」中(也可以在项目根目录下创建一个配置文件 .vscode/settings.json),添加以下配置:
{ "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "eslint.validate": [ "javascript", "javascriptreact", { "language": "typescript", "autoFix": true }, ], "typescript.tsdk": "node_modules/typescript/lib" }
-
Prettier修复格式错误
- 安装 npm install --save-dev prettier
- 创建 prettier.config.js
- vscode中 settings.json 配置
- eslint 检查代码格式问题避免与prettier冲突 禁用掉 eslint相关代码格式规则
使用 AlloyTeam 的 ESLint 配置
- npm install --save-dev eslint typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-alloy
三斜杠
- ///
- 作用:
- 声明文件中 代替import
- 书写全局变量声明文件时
// types/jquery-plugin/index.d.ts /// <reference types="jquery" /> declare function foo(options: JQuery.AjaxSettings): string; // src/index.ts foo({});
- 依赖一个全局变量的声明文件