ts问题小总结

7,051 阅读7分钟

ts问题小总结

  • 作为一个前端小白,刚刚开始学习TS的时候,遇到了许多疑惑,但是对于学过后端的朋友来说可能是小问题,所以酌情阅读~

区别

1.any和unkown的区别

  • any表示任意类型,可以被任何类型分配,也可以分配给任何类型
  • unkwon表示未知类型,可以被任何类型分配,不能分配给任何类型
  • 首先看any类型
// 1. 把any类型分配给其他类型
let val:any
let val_any:any = val;
let val_unknown:unknown = val;
let val_void:void = val;

let val_undefined:undefined = val;
let val_null:null = val;
let val_number:number = val;
let val_string:string = val;
let val_boolean:boolean = val;
// 报错:不能将类型“any”分配给类型“never”
// let val_never:never = val;


// 2.把其他类型分配给1any
val = '';
val = 1;
val = true;
val = null;
val = undefined;

// 报错:“unknown”仅表示类型,但在此处却作为值使用
// val = unknown;
// 报错:“never”仅表示类型,但在此处却作为值使用
// val = never;
// 报错:“any”仅表示类型,但在此处却作为值使用
// val = any;
// 报错:应为表达式
// val = void;
  • 然后看看unknown类型
// 1.把unknown类型分配给其他类型
let val: unknown;
let val_any:any = val;
let val__unknown:unknown = val;
// 报错:不能将类型“unknown”分配给类型“string”
let val_string:string = val;
// 报错:不能将类型“unknown”分配给类型“number”
let val_number:number = val;
// 报错:不能将类型“unknown”分配给类型“boolean”
let val_boolean:boolean = val;
// 报错:不能将类型“unknown”分配给类型“null”
let val_null:null = val;
// 报错:不能将类型“unknown”分配给类型“undefined”
let val_undefined:undefined = val;

// 2.把其他类型分配给unknown类型
val = '';
val = 0;
val = true;
val = undefined;
val = null;

// 和any一样,报错
val = void;
val = any;
val = unknown;
val = never;
  • 代码规范,any虽然可以代表任意类型,但是能不用就不要用,这是默认的代码规范问题,不要用成anyscript!
  • 与any任意类型相比,因为unknown是未知类型,所以只能进行!!,!,?,typeof,instanceof等有限操作

2. 数组和元组的区别

  • 如果数组的类型在[]前面,那么表示该数组全部都是该类型
  • 如果数组的类型在[]内部(严格限制类型和长度的元组),那么表示该数组的第x个元素是该类型
  • 首先,数组的类型在[]前面
    // 此时表示数组内部都是数字类型
    // let arr:number[] = [1,2,3];
    let arr:(number | string)[] = ['s',3,'a'];
    let arr:any[] = ['a',2,true];
  • 此时来看看数组的类型在[]内部的用法,此时就是元组
    // 报错:不能将类型“[number, number, number]”分配给类型“[number]”。源具有 3 个元素,但目标仅允许 1 个
    // let arr:[number] = [2,3,4];
    let arr:[number] = [2]; // 这个时候才是对的!
    // 表示多种类型的数组,但是严格限制长度和类型必须对照
    let arr:[string,number] = ['a',1];
    // 报错:不能将类型“string”分配给类型“number”
    // let arr:[string,number] = [1,'d'];
    // any元组也需要规定元素数量
    let arr:[any,any,any] = ['s',2,true];
  • 其实[string,boolean]这种声明形式指的是元组,也就是一个已知元素数量和类型的数组

3.索引签名和工具类型Record的区别

  • 其实Record工具类型的本质就是索引签名,不同之处只是用法,仅仅需要继承就可以了,不需要再写一遍
  • 索引签名的用法
interface inf{
    name:string;
    age:number;
    [k:string]:any;
}
let obj:inf = {
    name:'yiye',
    age:33,
    city:'foshan'
}
  • Record工具类型的用法(ts内置的工具类型)
interface inf extends Record<string,any>{
    name:string;
    age:number;
}
let obj:inf = {
    name:'yiye',
    age:33,
    city:'foshan'
}
  • Record工具类型的.d.ts声明文件源码是:
type Record<K extends keyof any, T> = {
    [P in K]: T;
};
  • 所以用法就是继承这个工具类型,然后泛型参数1是属性类型,参数2是属性值的类型

4.interface和type的区别

  • 如果在开发一个包或者要被继承,那么使用接口interface
  • 如果要定义基础数据类型或者进行类型运算,那么使用类型别名type
  • 不同点1:interface可以进行声明合并,type不可以
// 1.interface同名接口会自动进行声明合并
interface union{
    name:string;
}
interface union{
    age:number;
}
let u = {} as union;
// undefined
console.log(u.name);
// undefined
console.log(u.age);
// 但是使用其他属性,不会undefined,会报错:类型“union”上不存在属性“list”
console.log(u.list);

// 2.type类型别名不可以进行声明合并
// 报错:标识符“type_a”重复
// type type_a = number;
// type type_a = string;
  • 不同点2:type可以直接进行赋值运算,而interface不可以,必须先继承
// 1.type可以进行类型运算(只能把type赋值给type,不能把type赋值给其他类型变量)
type type_a = number;
type type_sum = type_a | string;

// 2. interface不可以,必须要通过继承
interface inf_a{
    name:string;
}
// “inf_a”仅表示类型,但在此处却作为值使用
// interface inf_a = inf_a;
// 正确做法是
interface inf_b extends inf_a{
    age:number;
}
let inf  = {} as inf_b;
// undefined undefined
console.log(inf.age,inf.name);
  • 不同点3:interface只可以用于对象和函数;type则可以用于对象,函数,基础类型,数组,元组
// 1.接口
// 对象
interface obj{
    name:string;
}
// 函数
interface func{
    (x:string): number;
}

// 2.类型别名type
// 对象
type type_obj = {name:string};
// 函数
type type_func = (x:string) => string
// 基础类型
type type_boolean = true;
type type_null = null;
// 联合类型
type type_union = string | number;
// 数组
type type_arr = number[];
// 元组
type type_tuple = [number,string];

5.enum和const enum

  • enum可以进行反向查找,所以遍历得到的长度是预计长度的两倍
  • const enum不可以进行反向查找,所以得到的是预计长度
// 1. enum
enum REVERSE{
    OK,
    NO 
}
// 0
console.log(REVERSE.OK)
// OK
console.log(REVERSE[0])
// OK
console.log(REVERSE[1])
// undefined,虽然是undefined,但是不会报错!
console.log(REVERSE[10])
// 遍历,得到枚举的值和反向查找的值
// of不可以,警告需要有[Symbol.iterator]方法
for(let item in REVERSE){
    // 0 1 OK NO
    console.log(item);
}

// 2. const enum
const enum ONE{
    OK,
    NO 
}
console.log(ONE.OK)
// 报错:只有使用字符串文本才能访问常数枚举成员。
// console.log(ONE[0]);

// 遍历
// 报错:"const" 枚举仅可在属性、索引访问表达式、导入声明的右侧、导出分配或类型查询中使用。
/* for(let item in ONE){

} */

应用

1. 类型键入

type User = {
    outer:string;
    // 内部使用一个数组
    innerList:{
        innerName:string
    }[]
}
// (property) outer: string
type userOut = User['outer'];
/* 属性数组
(property) innerList: {
    innerName: string;
}[] */
type userList = User['innerList'];
// 类型键入,获取数组的一个项
// number表示数组子项的类型是number
type userListItem = userList[number];
let item:userListItem = {
    innerName:'yiye'
}
// { innerName: 'yiye' }
console.log(item)

2 装饰器

function fun() {
    return function(target: any, key: string, descriptor: PropertyDescriptor) {
        console.log(target); // 该装饰器所在的类或函数  any: 类
        console.log(key); // 装饰器应用的变量名 string : getDecorator
        console.log(descriptor); // 装饰应用的变量的descriptor属性
        // {writable: true, enumerable: false, configurable: true, value: ƒun}
    };
}
class A {
    @fun()
    getDecorator() {
        console.log('测试');
    }
}
let obj:A = new A();
obj.getDecorator();

3. typeof

  • typeof 关键字在 JS 中用来获取变量的类型,运算结果是一个字符串(值)。而在 TS 中表示的是推算一个变量的类型
// 1.1 typeof变量,得到的是变量的类型
let str_1 = 'hello'
// string
console.log(typeof str_1);
type type_1 = typeof str_1;
let obj_1:type_1 = 'dd';
// dd
console.log(obj_1)
// 1.2 typeof常量,得到的是常量的值
const str_2 = 'abc'
// 虽然打印得到的是string
console.log(typeof str_2);
// 但是通过type赋值为类型
type type_2 = typeof str_2;
// 此时会报错
// 报错:不能将类型“"typeof常量得到的类型是常量的值"”分配给类型“"abc"”
// let obj_2:type_2 = 'typeof常量得到的类型是常量的值';
// 这个时候才正确
let obj_3:type_2 = 'abc';

// 1.3 typeof对象
interface person{
    name:string,
    age:number
};
let obj:person = {
    name:'yiye',
    age:11
}
// object
console.log(typeof obj)
// 本质上就是:type type_obj = person
type type_obj=typeof obj;

// 1.4 typeof函数
function foo(key:string){
    return key;
}
// type type_func = (key: string) => string
type type_func = typeof foo;

4.keyof

  • keyof关键字用来获取一个对象类型的所有key类型
type type_1 = {
    id:number,
    name:string
}
// keyof type_1得到 "id" | "name"
// 因为作为变量key_1的类型,所以key_1只能被赋值为"id" | "name"
let key_1: keyof type_1 = 'id'
// let key_1: keyof type_1 = 'name'
// 报错:不能将类型“"a"”分配给类型“"id" | "name"”
// let key_1: keyof type_1 = 'a'
console.log(key_1);

// keyof应用到泛型
// 表示传递的第一个值是对象,第二个值是对象的属性
function foo<T extends Object, K extends keyof T>(obj:T,key:K){
    console.log(obj[key]);
}
interface key{
    name:string,
    age:number
}
let obj:key = {
    name:'yiye',
    age:11
}
// yiye
foo(obj,'name');
// 11
foo(obj,'age');
// 报错:类型“"a"”的参数不能赋给类型“"name" | "age"”的参数
// foo(obj,'a');

本文使用 mdnice 排版