TS

144 阅读7分钟

1、参考:[TypeScript 学习之路_如何查看ts版本-CSDN博客] (blog.csdn.net/qq_42540989…)
本篇主要介绍TS的类型

1、一种由微软开发的自由和开源的编程语言,简称 TS2TypeScriptJavaScript 的超集,即包含 JavaScript 的所有元素,能运行 JavaScript 的代码,
并扩展了 JavaScript 的语法。
3、相比于 JavaScript,它还增加了 静态类型、类、模块、接口 和 类型注解 方面的功能,相对于 JavaScriptTypeScript 属于 强类型 语言,所以对于项目而言,会使代码更加规范,从而解决了大型项目代码的复杂性。
更易于大项目的开发。
4TS 是不能被浏览器直接识别的,所以在编译的时候,TS 文件会先编译为 JS文件。
5TSJavaScript的超集,用于解决大型项目的复杂性。
能在编译期间发现错误并纠正错误。
是强类型语言,支持静态类型和动态类型。
最终是被编译成JavaScript代码,使得浏览器可以理解。
支持模块、泛型和接口。
6JS:一种脚本语言,用于创建动态网页。
只能在运行时发现错误。
是弱类型语言,没有静态类型。
直接在浏览器使用。不支持模块、泛型和接口。

2、在不运行代码的情况下检测其中的错误称为 静态检查 。根据被操作的值的种类来确定是什么错误和什么不是错误,这成为 静态类型检查。

3、TS开发环境搭建:TS需要进行编译为JS,TS解析器使用node来制作的。安装:npm i -g typescript
查看版本信息:tsc -v

1、下载安装node.js
2、使用npm全局安装TS。进入命令行,输入:npm i -g typescript
3、创建一个ts文件
4、编译ts文件:tsc a.ts。会生成相应的js文件

4、ts的类型声明

类型声明:
       类型声明是TS非常重要的一个特点
       通过类型声明可以指定TS中变量(参数、形参)的类型
       指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错
       简而言之,类型声明给变量设置了类型,使得变量只能存储某种类型的值
       语法:
            let 变量:类型;
            let 变量:类型 = 值 ;
            function fn(参数:类型,参数:类型):类型{}
自动类型判断:
    TS拥有自动的类型判断机制。
    当对变量的声明和赋值是同时进行的,TS编译器会自动判断变量的类型。
    所以如果变量的声明和赋值时同时进行的,可以省略掉类型声明。
// 注意:声明变量类型的都是小写的(number\boolean\string)
// 表示声明一个变量 a ,同时指定它的类型为 string
// a 的类型为 string ,在以后的使用过程中 a 的值只能是 string
let a : string
a = '1000'

// 表示声明一个变量b,指定类型是 Number。同时进行赋值
let b : number = 1111
let b1 : boolean = false

// function fn(参数:类型,参数:类型):类型(params:type) {}
function sum(m:number,n:number) {
    return m + n
}
sum(123,456)

// 给返回值的类型进行指定
function fun(a2:number,b2:number):number {
    return a2 + b2
}
fun(1,2)
使用 tsc 编译好该文件后,发现代码中高亮显示(无法重新声明块范围变量“a”)。
原因:是因为在Commonjs规范里,没有像ESModule能形成闭包的模块概念,所有的模块在引用时都默认
被抛掷全局,因此当再次声明某个模块时,TS会认为重复声明了两次相同的变量进而抛错。
解决办法:关闭编译出来的js文件窗口,这样全局就只有一个变量了。

5、TS中类型

image.png

number:

// number 类型
let a : number = 1
let a1 = 2

string

// string 类型
let b : string = 'hello'
// 模板字符串
let b1 :string = `${b}`

boolean

// boolean 类型
const c:boolean = false

使用字面量进行类型声明

// 使用字面量进行类型声明
// 意味着把 m 赋值为 10,此时不能更改 m 的值
// 限制变量的值就是该字面量的值
let m : 10
let n : 'hello' | 'hi'
// n1 的值可以是字符串,也可以是数字
// 使用 | 来连接多个类型(联合类型)
let n1 :string | number

any类型

// any 类型.any表示的是任意类型,一个变量设置类型为 any 后相当于对该变量关闭了 TS 的类型检测
// 可以访问它的任何属性,可以将它分配给赋予 任意类型 的值
let a;  // 声明变量不赋值,等效于  let a: any;
// a 的类型不确定,ts会自动将其定义为隐式 any ,能够变成多种类型
a = 123;  // 未声明类型的变量虽然一开始被识别为 any 类型,但是经过赋值后,TS 会根据赋值类型来标识变量的类型
a = "pyy"
a = true
console.log("这是a: ", a)   // "这是a: ",  true 

// 定义的any类型,类型可以多次改变,可以给任意类型赋值, 也可以被任意类型赋值,还能赋值给其他类型的变量
let a1: any;
a1 = 123;
a1 = "pyy";
a1 = false;
a1 = a;
console.log("这是a1: ", a1)  // "这是a1: ",  true 

let b: string = "pyy"; 
b = a1; 
console.log("这是b: ", b) // "这是b: ", true

unknown类型

// unknown 类型:表示未知的类型
// 未知的类型,TS中所有基础类型的父类型,所有基础类型都能赋值为 unknown类型。
// 但 unknown 是无法给 unknown 之外 的(包括 any)其他类型变量赋值的
// unknown 实际上是一个类型安全的 any。
// unknown 类型的变量不可以直接赋值给其他变量
let c1: unknown;
c1 = "wyz&pyy";
c1 = 2;
console.log("这是c1: ", c1)   // 这是c1:  2
c1 = false;
console.log("这是c1: ", c1)   // 这是c1:  false  

// unknown 类型直接赋值给其他变量的方法
// 先使用 typeof 进行类型判断
let num1: unknown = 666;
if(typeof num1 === "number") {
    let num2 = num1
    console.log(num2);  // 666
}

// 类型断言:可以用来告诉解析器变量的实际类型
let num1: unknown = 666;
// 把 num1 当作number
// 变量 as 类型
let num2 = num1 as number;
// 或
// <类型>变量
let num3 = <number>num1;

any 和 unknown 的区别

// any 和 unknown 的区别
// unknown类型会更加严格:在对 unknown类型的值执行大多数操作之前,不能直接赋值给其他变量,必须将这个 
// unknown 类型的变量断言为具体的类型,才可以继续使用。
// 而在对 any类型的值执行操作之前,会绕过类型检查,直接可用。
let foo: any = 123;
console.log(foo.msg); // 符合TS的语法
let value1: unknown = foo;   // OK
let value2: any = foo;      // OK
let value3: string = foo;   // OK

let bar: unknown = 456; // OK 
console.log(bar.msg); // Error
let value4: unknown = bar;   // OK
let value5: any = bar;      // OK
let value6: string = bar;   // Error

// 因为bar是一个未知类型(任何类型的数据都可以赋给 unknown 类型),所以不能确定是否有msg属性。不能通过TS语法检测;
// 而 unknown 类型的值也不能将值赋给 any 和 unkown 之外的类型变量
// 总结: any 和 unknown 都是顶级类型,但是 unknown 更加严格,不像 any 那样不做类型检查,反而 unknown 因为
// 未知性质,不允许访问属性,不允许赋值给其他有明确类型的变量

void类型

// void 类型:没有值或undefined
// 可以用来声明变量,但只能作用在 undefined 身上,null 也不可以。
// 只有 tsconfig.json 中 strictNullChecks 属性设置为 false 时,null 才能赋值给 void。
// 一般用于当一个函数 没有返回值时,TS 会默认他的返回值是 void 类型
function fn():void {
    console.log(1230);
    return undefined
}

never类型

// never 类型
// 在TS中使用 never类型来表示不应该存在的值的类型,比如说:
// 1、一个抛出异常的函数
// 2、一个永远不会返回的函数的返回值类型
// *注意:never 类型是任何类型的子类型,可以赋值给任意类型。但是没有类型是never类型的子类型,即使是any类型 也不能赋值给 never 类型
function fn2():never {
    throw new Error("报错");
}

object类型

// object 类型
// 方式一:
// object 表示一个js对象
let a :object;
a = {
  name:"pengyuyan",
  age:18
};
a = function () {}

// 方式二,直接限制为对象 Object
let b:{};
b = {
  name:"pengyuyan",
  age:18
}

// 定义对象时,需要定义出对象中有哪些属性,每一个属性的值是什么类型,指定的属性的个数,多一个属性也不行,
// 少一个属性也不行
const obj1: { age: number, name: string } = { age: 18, name: 'pengyuyan' };

// 如果想指定属性是可选的(可有可无)
let obj3:{
  name:string,
  age?:number
}
obj3 = {
  name:"hello"
}

// 表示的是属性名是字符串,属性值是any类型
// js中的属性名都是字符串
let a1 :{name:string,[propName:string]:any}
a1 = {name:'cds',age:'18岁',hudf:false}
// 创建的 d 是一个函数
// 箭头函数,此时a是number类型,b也是number类型,同时返回值也是number
// 声明的函数在调用的时候,参数个数 要和 声明时候的参数 个数 保持一致
// 定义函数语法:(形参:类型,形参:类型...) => 返回值
let d : (a:number,b:number)=>number
d = function (n1:number,n2:number) {
    return n1+n2
}
console.log(d(1,2));

//没有返回值的函数可以用void声明
// 参数类型定义
const f1 = (name:string,id:number): void => {
    console.log("我是没有返回值的箭头函数");
};
  
function f2(name:string,id:number):void{
    console.log("我是没有返回值的普通函数");
}
  
// 有返回值的箭头函数声明是这样的
// 返回类型注释
const f3 = (): string => {
    return "pengyuyan=>"
};
  
//有返回值的普通函数声明是这样的
function f4():string{
    return "pengyuyan"
}
  
//函数表达式的双向限定
//上述f1其实只对=右侧做了限制,对左侧并没有
//完善一点,可以这样 => 用来表示函数的定义,左输入类型,需要用括号括起来,右输出类型 
const f5:(name:string,id:number)=>void = (name:string,id:number): void => {
    console.log("我是没有返回值的箭头函数");
};

// 函数的可选参数
// 注意可选参数要在确定参数后
function f6(name:string,id?:number):string{
    return "pengyuyan"
}
  
//函数参数默认值
function f7(name:string,id:number=1):string{
    return `${name}--${id}`
}
//此时可选参数不必一定在确定参数后,但是调用有问题
function f8(name:string,sex?:string,age:number=1):string{
    return `${name}--${age}--${sex}`
}
console.log(f8('pengyuyan','male',18))  // "pengyuyan--18--male"
  
//剩余参数
// :number 表示该函数的返回值类型为数字数组
function f9(...arr:number[]):number[]{
    return arr
}
console.log(f9(1,2,3,4,5)) // [1,2,3,4,5]

Array类型

// Array 数组类型
// TS 中要求数组中的每一项必须是 同一个数据类型。
// 语法:
    // 变量名: 数组中数据的类型[ ] = 数组值
    // 变量名: Array<数组中数据的类型> = 数组值
// number[]  表示数值数组
const arr1: number[] = [1, 2, 3];
const arr2: Array<number> = [1, 2, 3];
const arr3: Array<string> = ['1', '2']
const arr4: Array<number> = [1, 2, '3'] // error 必须是同一个数据类型

//如果想要是数字类型或字符串类型,需要使用 |
const arr5: Array<number | string> = [1, 2, '3']

tuple元组类型

// 元组:固定长度的数组
// 元祖可以理解成一个任意类型并且长度有限的数组 。元组中,允许一个数组中保存多个类型的数据。
// *注意:数组中的值与元组类型必须:数量、位置、类型都要对应
const arr5: [string, number] = ['1', 2];
let arr6: [string, number];
// arr6[0] = 'pengyuyan';
// arr6[1] = 18;
// arr6[0].slice(1);
// arr6[1].toFixed(2);

// 元组越界
// ts允许向元组中使用数组的push方法插入新元素(但不允许访问)
const arr7: [string, number] = ['pengyuyan', 2];
arr7.push(3); // 正常运行
console.log(arr7); //  正常运行 ['pengyuyan',2,3]
console.log(arr7[2]); // ERROR 访问新插入的元素会报错

enum 枚举

// Enum(枚举):可以定义带名字的常量
// 枚举的类型只能是 string 或 number。定义的名称不能为关键字
// ------------按枚举成员分类-----------------
// I、数字枚举(是数字类型。如果有默认值,会影响到后面的值。支持反向映射)
// 定义了一个数字枚举,Up使用初始化为 1。其余的成员会从 1开始自动增长。
enum Direction {
    Up = 1, // 1
    Down, // 2
    Left, // 3
    Right // 4
}
enum Direction {
    Up, // 0
    Down = 3, // 3
    Left, // 4
    Right // 5
}
 
// 当不在乎成员的值的时候,这种自增长的行为是很有用处的,但是要注意每个枚举成员的值都是不同的。
enum Direction {
    Up,  // 0
    Down, // 1
    Left, // 2
    Right // 3
}

// 如果未手动赋值的枚举项与手动赋值的重复了,TypeScript是不会察觉到这一点的,但建议尽量避免
enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};
const date: { id: number, name: string, days: Days } = { id: 1, name: '周三', days: Days.Wed }
console.log(Days["Sun"] === 3); // true
console.log(Days["Wed"] === 3); // true
console.log(Days[3] === "Sun"); // false
console.log(Days[3] === "Wed"); // true

// II、字符串枚举
//在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化
// 必须要有默认值。不支持反向映射
enum Direction {
    Up = "UP",
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT",
}
const a: Direction = Direction.Up  //a = UP
const b: Direction = Direction.Down   //b = DOWN
// 由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。 
// 字符串枚举允许你提供一个运行时有意义的并且可读的值,独立于枚举成员的名字。

// III、异步枚举(不建议):将数字枚举与字符串枚举混用,不建议


// ---------按声明方式----------
// I、普通枚举
enum Color {Red, Green, Blue = "blue".length};
// 值由计算所得变为计算所得项 如,"blue".length 就是一个计算所得项
enum Color {Red = "red".length, Green, Blue};
// 上述代码会报错,因为Red是计算项,而Green紧接其后却无法获取初始值.
// 根据官方定义,不带初始化器的枚举要么被放在第一的位置,要么被放在使用了数字常量或其它常量初始化了的枚举后面。

// II、常量枚举(通过 const enum 定义的枚举)
const enum Direction {
    Up,
    Down,
    Left,
    Right
}
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
// var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];

// III、外部枚举
// 用来描述已经存在的枚举类型,当前环境中已经存在的对象的,这个对象可以存在任意的地方,但是一定是已声明的。
// 注意:不支持反向映射
// 参考:https://stackoverflow.com/questions/28818849/how-do-the-different-enum-variants-work-in-typescript
declare enum Enum {
    A = 1,
    B,
    C = 2
}

// IV、使用枚举
// 通过 枚举的属性 来访问 枚举成员
// 通过 枚举的名字 来访问 枚举类型
enum Response {
    No = 0,
    Yes = 1,
}

function respond(recipient: string, message: Response): void {
    // ...
}

respond("Princess Caroline", Response.Yes)