likes
comments
collection
share

一、TS核心语法+各种实战应用(上)

作者站长头像
站长
· 阅读数 14

1.Typescript定义

融合了后端面向对象思想的超级版的JavaScript语言
1. TypeScript: 静态类型检查器 
2. TypeScriptJS的超集 为强类型 
3. JavaScript为弱类型 
4. TS === JS + 类型 js有的ts都有 
5. TS 规范了 JS类型声明 使之更加严谨

2.环境准备

npm init -y // 初始化一个package.json

yarn/npm/pnpm add typescript -D === --save-dev  // 当前项目目录安装

yarn/npm/pnpm add typescript -g                 // 全局安装

tsc --init  // 生成tsconfig配置文件  

tsc demo.ts  // 终端运行ts文件                   // 将ts转为js浏览器可识别的代码

3.TypeScript的优势

优势一

编译时静态类型检测:函数或方法传参或变量赋值不匹配时,会出现编译借误提示,规避了开发期间的大早低级借误,省时,省力。

let str:string = "string"  // 指定了string
str = 3  //  error 只能赋值string类型的值

let arr:string[] = []  // 指定字符串数组类型
arr.forEach(()=>{}) 

let arr:string = '21'  // 指定字符串
arr.forEach(()=>{})  // error 字符串是没有forEach方法的

优势二: 自动提示更清晰明确

let cus = {
	name: 'zhangsan'
	degree: 'guardcard',
	phone: '111'
}
interface ICus {
    name: 'zhangsan',
    degree: 'guardcard',
    phone: '111'
}
cus/ICus 使用某个属性的时候会明确的提示 这个对象下有哪些属性

优势三

引入了泛型和一系列的 TS 特有的类型

优势四

强大的 d.ts 声明文件:声明文件像一个的目录一样,清晰直观展示了依赖库文件的接口,type类型,类,函数,变量等声明.

优势五

轻松编译成JS文件: 即使TS文件有错误,绝大多数情况也能编译出JS文件

优势六

灵活性高: 尽管TS是一门强类型检查语言,但也提供了any类型 和 as any 断言,这提供了TS的灵活度

4.类型注解 与 类型推导

类型注解:  声明变量的时候就进行类型约束
    let num:number = 3;
    let str:string = '3';
    let thruly:boolean = true;
    let isNot:null = null;
    let und:undefined = undefined;

类型推导:  通过右边变量的值推导出类型
    let num = 30;   // 就会自动推导出变量的类型

俩者之间的区别: 
    1.类型注解 约束类型之后不能更改为其他的类型
    2.类型推导 根据自己写的值就会推导出对应的类型

5.Ts编译和编译优化

先打开tsconfig,json
    "rootDir": "./src",     // 输入目录
    "outDir": "./dist",    // 输出目录 用来存放src文件编译之后的文件目录
    控制台执行tsc  将src文件编译到dist目录下

6.常用的24种TS类型

1.基本类型:

    number
    string
    boolean
    symbol
    null
    undefined

2.根类型:

其他类型的父类
    Object: {}       //  除了null,undefined  不能赋值给它,其他的都可以
    Object === {}    //  {} 为 Object 的简写

3.对象类型

    Array
    function
    object

4.枚举类型

4-1.枚举定义
enum(枚举)
    用来存放一组固定的常量的序列
    
为什么要使用enum(枚举)?
    解决多次if/switch 判断中值的语义化的问题
      
常量的局限性
    1.方法参数不饿能定义为具体类型,只能初级使用 number, string 基本类型替代
    2.降低了代码的可读性,可维护性
    
没有ts时候使用常量解决
    enum(枚举)正好可以解决  使用常量带来局限性

    const Status = {
        MANAGER_ADUIT_FAIL: -1;
        NO_ADUIT: 0,
        MANAGER_ADUIT_SUCCESS: 1,
        FINAL_ADUIT_SUCCESS: 2
    }
    
    class MyAduit {
        getAduitStatus(status: number): void {
            if(status === Status.NO_ADUIT) {
                console.log('无审核');
            }else if (status === MANAGER_ADUIT_SUCCESS) {
                console.log('管理审核通过');
            }else if (status === Status. FINAL_ADUIT_SUCCESS) {
                console.log('老板审核通过');
            }
        }
    }
4-2.字符串枚举
// 数字枚举
enum Week {
    // Monday 初始值为1  后面的每一项在 Monday=1的基础上逐渐递增
    Monday = 1,
    Tuesday, // 2
    Wensday, // 3
    ThirsDay,// 4
    Friday,  // 5
    Sarturday,// 6
    Sunday   // 7
}
// 取值方式
console.log(Week.Monday); 
console.log(Week["Monday"]); // 由key 取值
console.log(Week[1]);        // 由值取key
4-3.数字枚举
// 字符串枚举
enum Week1 {
    Monday = "Monday",
    Tuesday = "Tuesday", 
    Wensday = "Wensday", 
    ThirsDay = "ThirsDay",
    Friday = "Friday",  
    Sarturday = "Sarturday",
    Sunday    = "Sunday"
}
console.log(Week1.Monday); 
console.log(Week1["Monday"]); // key 取值
4.4enum(枚举)的好处
    1.有默认值和可以自增值,节省编码时间
    2.语义更清晰,可读性增强
    3.因为枚举是一种值类型的数据类型,方法参数可以明确参数类型为枚举类型

5.其他特殊类型:

5-1.any
应用场景: 
    1. 自定义守卫
    2. 需要进行 as any 类型断言的场景
5-2.unknown
应用场景: 
    1. 一般用作函数参数: 用来接受任意类型的变量实参,但在函数内部只用于再次传递或输出结果,不获取属性场景
    const getData = (data: any) => {
    console.log(data.name);  // unknown 的时候 “data”的类型为“未知”
                             // any 时候正常的 .name
    }
    getData({ name: "张三", age: 23 })
    getData([ 3, 4 ])
5-3.any和unknown的区别
相同点:
    1. anyunknown 可以是任何类的父类,所以任何类型的变量都可以赋值给any类型类型或者unlnown类型的变量
不同点:
    1.any也可以使任何类的子类, 但unknown不可以,所以any类型的变量都可以赋值给其他类型的变量
        let data: any = ['321', '1']
        let num: number = data
    2.不能拿unknown类型的变量来获取任何属性和方法,但any类型的变量可以获取任意名称的属性和任意名称的方法
5-4.never
    定义: 什么都没有的类型
    使用never避免出现未来扩展的类型没有对应类型的实现,目的就是写出类型绝对安全的代码
    
    type DataFlow = string | number;
    const data = (dataFlow: DataFlow) => {
        // 类型收窄 将原来string和number类型 收窄为 string类型
        if (typeof dataFlow === "string"){
            console.log("字符串类型:",dataFlow.length)
        } else if (typeof dataFlow === "number") {
            console.log("数值类型:",dataFlow.toFixed(2));
        } else {
            // else 区间 是预留出来 未来类型的空间 
            // 如果 DataFlow 还有一个boolean那么else区间的类型就是布尔否则就是 never
            let data=dataFlow  // never
        }
    }
    data("123”);
    data(314);

5-5.void

当涉及函数的返回值时,void 类型表示该函数不返回任何值。它类似于 JavaScript 中的没有明确返回值的函数,默认返回值为 undefined

在 TypeScript 中,可以通过在函数声明时将其返回类型设置为 void,来表明这个函数不会返回任何实际值。这对于执行一些操作而不需要返回结果的函数非常有用。例如:

function Fun(text: string): void { 
    console.log(text); 
}

在这里,Fun 函数将字符串文本作为参数,但它不返回任何值。它只是将文本打印到控制台,所以返回类型被设置为 void

当调用 Fun 函数时,它不会返回任何值,它只是执行其中的代码并打印消息

Fun("Hello, TypeScript!"); // 输出:Hello, TypeScript!

如果您有一个函数执行某些操作,但是不需要其结果,那么可以将其返回类型设置为 void

需要注意,void 类型只适用于函数的返回值,而不适用于变量。这是因为 TypeScript 的类型系统通常用来描述值的形状和用途,而不是变量的状态。

5-6.tuple元祖可变元祖

暂未更新 在学习

6.合成类型

6-1.联合类型
    let str: string | number = 3;  // 或的关系 |  可以使string也可以使number
6-2.交叉类型
Type 可以定义任意一种数据类型
    type Obj1 = { username: string }
    type Obj2 = { age: number }
    let obj1: Obj1 = { username: "abc"}
    let obj2: Obj2 = { age: 23 }
    // & 并集 合并
    let obj3: Obj1 & Obj2 = {     // obj3 {username: string, age: number}
            username: "wangwu", 
            age:40
    }    
    console.log(obj3);

7.字面量数据类型

    type A = 'success' | 'error' | 0 | 1 | true | false  //  应用的时候只能选择这几个值
    // 示例
    type Flag = 0 | 1 | 'success' | 'error'  // 只能传入规定的字面量值
    const isStart = (isFlag: Flag) => {
        if (isFlag) {
            console.log('open')
        } else {
            console.log('close')
        }
    }
    isStart(1)

8.深入理解接口 + 真实应用场景

  • 另一种定义对象类型的类型
  • 接口应用场景
    • 一些第三方包或者框架底层源码中有大量的接口类型
    • 提供方法的对象类型的参数时使用
    • 为多个同类别的类提供统一的方法和属性声明
8-1.如何定义接口
    // 定义一个信息类型
    interface InfoType {
        name: string;
        age: number;
        gender: string;
        sayHi(): void;
    }

    // 定义一个变量来使用它
    const info: InfoType = {
        name: 'John',
        age: 36,
        gender: 'Female',
        sayHi() {
            console.log('sayHi');
        }
    }
8-2.接口继承
    // 动物类型  抽离动物共有的属性
    interface Animal {
        name: string;
        age: number;
        animalType: string;
        sex: '公' | '母';
    }

    // DogType 继承了 Animal 的属性类型
    interface DogType extends Animal {
        guradHome(): void;
    }

    // test1 属性中必须包含DogType 与 Animal 合并之后的所有属性
    // 除非带有可选属性 ? 来表示  如: animalType?: string;
    const test1: DogType = {
        guradHome() {}, 
        name: '二狗', 
        age: 1, 
        animalType: 'run', 
        sex: '公'
    }
8-3.为多个同类别的类提供统一的方法和属性声明
  • 方便统一管理
    interface List {
        add(): void;
        remove(): void;
    }

    class ArrayList implements List {
        add(): void {
            throw new Error("Method not implemented.");
        }
        remove(): void {
            throw new Error("Method not implemented.");
        }
    }

    class LinkedList implements List {
        add(): void {
            throw new Error("Method not implemented.");
        }
        remove(): void {
            throw new Error("Method not implemented.");
        }
    }
8-4.可索引签名,及2个容易忽略的细节
    // 可索引签名
interface Product {
    name: string;
    price: number;
    account: number;
    // [] 固定写法 x代表未知的属性名 && 为字符串类型
    // 不明确的时候写成any 明确的时候直接写但是 要兼容上面的类型 可使用联合类型  string | number
    [x: string]: any; // string | number 未知属性可索引签名
}

let p: Product={
    name: "张三",
    price: 1000,
    account: 100,
    // 符合 [x: string]: any; 类型的
    // 以下这种情况都符合
    description: "描述",
    stock: 10000,
    [Symbol("buy")]: 23,
    1: "gold",
    true: true,
}
8-5.索引访问类型,及深入扩展
    // 索引访问类型
    const symid = Symbol("productSc")
    interface Product {
        [symid]: number | string;
        name: string;
        price: number;
        account: number;
        buy(): void
    }

    // 接口重名可合并---重载
    interface Product {
         age: number,
    }

    type A = Product["buy"]  // "" 中得到的是一个类型  Product也是类型
    type B = Product["price"|"name"] // 联合类型
/**
 * @description 获取symid 的类型该如何获取
 */
    type S = Product[symid]  // 错误 symid 因为本身就表示变量值
    type S = Product[typeof symid]  // typeof 可以获取js变量类型的操作符

/**
 * @description 想要获取接口里面所有的属性名
 */
    type Pkeys = keyof Product // === "name" | "price" | "account" | "buy" | typeof symid
    let key: Pkeys="account"
    // let key1: "name" | "price" | "account" | "buy" | typeof symid = "account"

    // 使用泛型来获取
    type AllKeys<T> = T extends any?T:never
    type key2 = AllKeys<keyof Product>

9.null和undefined + 相关重要细节

9-1.javascript中
null 
    表示什么都没有,表示一个空对象引用
    let obj = null
    console.log(typeof null) // object
    
undefined
    声明一个变量,但是没有赋值,该变量值为undefined
    var a
    console.log("a", a)
    console.log(typeof undefined) // undefined
9-2.Typescript中
    let str: string
    console.log(str); // 没有赋值的情况下回报错

    let str1: string | undefined = undefined;  // 可以赋值undefined 也可以赋值字符串
    console.log(str1);  // 使用联合类型  是可以允许为空的

    let str2: string = undefined; // 在ts中是不允许将undefined赋值给string类型
    // 有一种方式可以解决当前报错
    // 在tsconfig.json中  严格模式下strict 的 strictNullChecks
    // 设置为false即可解决,但是不建议
    // "strict": true,                                      
    // "strictNullChecks": true,    

    // undefined 应用
    // 不加? 时候会报错(必须要有一个参数)  ?表示参数可传可不传
    const data = (dt?: string) => {  
        // 1. !  非空断言  表示我确定这里面一定不等于空
        dt!.toString();
        // 2. if 判断
        if(dt) dt.toString()
    }
    data()

    // 可以接收undefined的三种类型  null 也是同理
    let d1: unknown = undefined
    let d2: any = undefined
    let d3: undefined = undefined
    
    // 可以接收null的三种类型
    let c1: unknown = null
    let c2: any = null
    let c3: undefined = null

10.看似简单地取值为什么会抛出错误

let obj = { userName: '张三', age: 12 }
// 一个不固定的字符串做为固定对象obj属性名的索引是不可以的,使用const改为常量就可以了
let username = 'userName'  // 错误
const username = 'userName' // 正确
let test = obj[username]
错误: 
    1.元素隐式具有 "any" 类型,因为类型为 "string" 的表达式不能用于索引类型 
        "{ userName: string; age: number; }"
    2.在类型 "{ userName: string; age: number; }" 上找不到具有类型为 "string"
        的参数的索引签名
let obj:object = { userName: '张三', age: 12 }
const username = 'userName'
let test = obj[username]  // objject上就不存在userName
错误: 
    元素隐式具有 "any" 类型,因为类型为 ""userName""的表达式不能用于索引类型 "{}"
    类型“{}”上不存在属性“userName”

11.interface 与 Type的区别?

语法差异

  • interface 使用 interface 关键字定义
  • type 使用 type 关键字定义

合并能力

  • interface 接口可以重名 type不可以重名
  • interface 同名接口会被合并为一个接口 type会产生冲突 发生报错565656565656

对象类型 vs 声明类型

  • interface 用于定义对象类型,可以描述对象的结构``属性方法 也可以实现继承(extends)
  • type 支持联合类型 '|', 交叉类型 '&'

12.TS函数和TS函数类型

// 1.
//函数返回值这里指定的是返回number,但是ts其实会根据你返回的会自动进行类型推导
function info(name: string, age: number): number { 
  console.log(name, '===name===')
  console.log(age, '===age===')
  return 4
}
info('122', 12)

// 2.
// 函数表达式类型写法  会根据右边函数推导并且返回给 变量info
const info = (name: string, age: number): number => {
  console.log(name, '===name===')
  console.log(age, '===age===')
  return 4
}

// 3.
// 定义指定函数的类型 (比较麻烦)
const info: (name: string, age: number) => number =
     (name, age) => {
      console.log(name, '===name===')
      console.log(age, '===age===')
      return 4
    }
// 使用type将类型抽离
type InfoFun = (name: string, age: number) => number
const info: InfoFun = (name, age) => {
  console.log(name, '===name===')
  console.log(age, '===age===')
  return 4
}

// 关于reset参数
// reset这里给了any 根据实际情况还是要自己组装正确的数据类型 少用any
function info(name: string, age: number, ...reset: any) {
  console.log(name, '===name===')
  console.log(age, '===age===')
  console.log(reset, '===reset===')
  return 4
}
info('122', 12, '2442', 12, 421)  // 则不受限制


// 像这样
type ParamsType = {
  person: string
  sex: string
  bool: boolean
}
function info(name: string, age: number, ...reset: ParamsType[]) {
  console.log(name, '===name===')
  console.log(age, '===age===')
  console.log(reset, '===reset===')
  return 4
}
info('122', 12, { person: '张三', sex: '男', bool: true })