大厂面试TS(上)

2022-07-11  本文已影响0人  Famous

一、TS面试基础知识

1、什么是TS?

面向项目:
TS - 面向解决大型的复杂项目、架构、代码维护复杂场景
JS - 脚本化语言,用于面向简单页面场景

自主检测:
TS - 编译时,主动发现并纠正错误
JS - 运行时,执行报错

类型检测:
TS - 强类型语言,支持动态和静态的类型检测
JS - 弱类型语言,无静态类型选项

运行流程:
TS - 依赖编译,依靠编译打包实现在浏览器端的运行
JS - 可直接在浏览器端运行

复杂特性:
TS - 模块化、接口、泛型

2、安装运行

  npm install -g typescript
  tsc -v
  tsc xxx.ts

举栗: 问: 在字符串后面加上:string,代码运行时,这个string还存在吗? 有什么办法在运行时改变这个string吗?
答案: 不存在了,也没必要去修改这个string,它是在编译时为变量指定了一个类型。

3、TS 基础类型和语法

// es
let a = false;
// ts
let a:boolean = false;
// 统一方式 & <> 方式
let classArr: string[] = ['1', '2'];
let classArr: Array<string> = ['1', '2']
let tupleType:[string, boolean] = ['1', true]
   // 数字型枚举 - 默认从0开始,一次递增
  enum Store {
      BAD, // 不写默认0分
      GOOD, // 不写默认1分
      NICE=10, // 写默认10分
  }
  let score: Score = Score.BAD;
  // 字符串枚举 - 默认从0开始,一次递增
  enum Store {
      BAD = 'BAD', 
      GOOD = 'GOOD',
      NICE = 'NICE', 
  }
  // 反向映射
   enum Store {
      BAD, // 不写默认0分
      GOOD, // 不写默认1分
      NICE=10, // 写默认10分
  }
  let scoreName = Score[0] // 结果为'BAD'
  let scoreValue = Score['BAD'] // 结果0
  // 异构
  enum Enum{
    A,     // 0
    B,     // 1
    C = 'C',
    D = 'D',
    E = 8,
    F       // 9
  }
  // 面试题: 异构类型每一项的枚举值 => 进而问如何手写一个异构枚举??
  let Enum;
 (function (Enum) {
   // 正向
   Enum['A'] = 0;
   Enum['B'] = 1;
   Enum['C'] = 'C';
   Enum['D'] = 'D';
   Enum['E'] =8;
   Enum['F'] = 9;

   // 逆向
   Enum[0] = 'A';
   Enum[1] = 'B';
   Enum[8] = 'E';
   Enum[9] = 'F';

  })(Enum || Enum = {})
   let anyValue:any = '1';
   anyValue = false;
   let value1:boolean = anyValue // OK 使用前无需判断any类型
   let unknownValue: unknown = '1';
   unknownValue = 123;
   unknownValue = false;
   let aa:unknown = unknownValue // OK 
   let bb:any = unknownValue // OK 
   let cc:boolean = unknownValue // OK
   let cc:string = unknownValue // Error  使用之前要先判断类型是否符合

上述两种方式any 和unknown 有何区别??
1、任何类型都可以是any类型,ts 允许any类型的值进行任何操作,对它一路绿灯,等于免检标签。可以访问任意属性和方法。
2、任何类型都可以是unknown类型,一旦打上标签,会被重点检查,只要类型检查通过了才能进行操作。

说人话?? 举栗如下::

// 从后台拿到数据,不知道类型,按照不同类型走不同的逻辑处理
let a:unknown = JSON.stringify({result: []})
if (typeof a === 'string'){
    let b = JSON.parse(a)
} 
// 或者 使用断言
let score:unknown = 99.8
let result = Math.round(score as number); // 这要求运行时的score必须是number才能过检查
let result1 = Math.round(<number>score); // 这是另一种写法 
// 比如没有返回值的函数
function voidFunction():void{
   console.log('void')
}
// 比如专门用来报错的函数
function errMsg(msg:string):never{
   throw new Error(msg)
}
// 或者永不会结束的函数
function infiniteLoop:never{
     while(true) {  }
}

4、接口 - interface

// object - 非原始类型
interface ObjectConstructor{
    create(o:object | null):any
}
const proto = {}
Object.create(proto);
Object.create(null);
Object.create(undefined); // Error
// Object 
// Obect.prototype 上的属性保留了
interface Object {
    constructor: Function;
    toString():string;
    toLocaleString():string;
    valueOf():Object;
}
// 定义了Object 类属性
interface ObjectConstructor{
    new(value:any):Object;
    readonly property:Object;
}
// {} - 定义真正的空属性对象
const obj = {}
obj.prop = 'props'; // Error

obj.toString(); // OK
// 描述对象内容
interface Class1 {
  // 只读
  readonly name:string;
  age: number;
}
let Wang = {
     name: 'Famous',
     age: 11
}
// 面试题 readonly  与 js 的引用操作类型不同 < = > const
let arr:number[] = [1,2,3,4]
let ro: ReadonlyArray<number> = arr;
ro[0] = 12 // ok吗? 
ro.push(5); // ok吗?
ro.length = 10; // ok吗?
arr = ro; // ok吗
// 答案是全部报错
// 任意可添加属性
interface Class1 {
  readonly name:string;
  [propName:string]:any; 
}
let c1 = {name: 'JS'}
let c2 = {name: 'TS', level: 1} // level 就是后续新增的属性

5、交叉类型 - &

// 合并
interface A {
    inner: D;
}
interface B {
    inner: E;
}
interface C {
    inner: F;
}
interface D {
    d: boolean;
}
interface E {
    e: string;
}
interface F {
    f: number;
}

type ABC = A & B & C;
let abc:ABC = {
    inner:{
       d:false;
       e: 'className',
       f: 5     
   }
}
// 合并冲突
interface A {
    c:string;
    d:string;
}
interface B {
    c:number;
    d:string;
}
type AB = A & B
let ab: AB  // 此时 c 是string 也是number, 变成never 编译时会直接报错

6、断言 - 类型声明和转换(开发者和编译器做了一个告知交流)

let anyValue:any = 'hi Lu'
// 尖括号形式
let anyLength:number = (<string>anyValue).length;
// as
let anyLength:number = (anyValue as string).length;
// 非空判断 - 只确定不是空
type A = () => number;
const start = (param: A | undefined){
  // 业务逻辑
  // if (额外判断逻辑){
      let time = param!();  // 具体类型待定,但是非空确认
   }
}
// 面试题
const tsClass:number | undefined = undefined;
const aa:number = tsClass!
console.log(tsClass) // 做好了非空判断吗?
// 上述回转义成为
const tsClass = undefined;
const aa = tsClass // undefined

// 结论 尽量不要在赋值的时候去断言

// 肯定断言 - 肯定化保证赋值
let score:number;
startClass();
console.log(score) // 使用前赋值
function startClass(){
  score = 5;
}
// 应该这样写 let score!:number; 
// 上述代码一般要在写score时就赋值,而不是等后面再赋值,但是写了断言 ,提前打好招呼,示意等下会有赋值,编辑器就不会报错了。

7、类型守卫 - 语法规范范围内,额外的确认

// in - 定义属性场景下内容的确认(类不适合合并,走导流)
interface Teacher {
  name: string;
  courses:string[];
 }
interface Student {
  name: string;
  startTime:Date;
 }
type ClassA =  Teacher | Student
function startCourse(cls:Class){
    if ('courses' in cls){
        console.log('Courses:' + cls.courses)
    }
    if ('startTime' in cls){
         console.log('startTime:' + cls. startTime)
   }
}
// typeof / instanceof - 类型分类场景下的身份确认
function class(name:string, score:string | number){
     if (typeof score === 'number'){
         return 'teacher:' + name + ':' + score;
     } else if (typeof score === 'string'){
        return 'student:' + name + ':' + score;
     }
}
// instanceof
const getName = (cls:Class){
  if (cls instanceof Teacher){
      return cls.courses
  } else if (){
     return cls.startTime
  }
}
// 自定义类型 起个别的名字,更好认一点
 const isTeacher = function(cls: Teacher | Student):cls is Teacher {
   return 'courses' in cls
}
const getName = (cls:Teacher | Student)=>{
    if (isTeacher(cls)){
         return cls.courses
    }
}

8、TS 进阶

   class Course {
     start(name:number, score:number):number;
     start(name: string, score:string):string;
     start(name: string, score: number): string;
     start(name: number, score: number): string;
     start(name: Conbinable, score: Conbinable){
          if (typeof name == 'string' || typeof score == 'string') {
               return 'student:' + name + ':' + score;
          }
     }
   }
   const course = new Course();
   course.start('小红', 5) 
    function startClass <T, U>(name: T, score: U):T{
        return name + score;
    }
    console.log(startClass<Number, String>('小红', 5))
   // T、U、K - 键值、V - 值 、E - 节点、元素
    // 1、类装饰器
    function decorator1(target:Function):void{
        target.prototype.startClass = function():void{
           // 通用功能
       }
     } 
   // 2、属性装饰器
    function propsWrapper(target:any, key:string){
       // 属性的统一操作
      Object.defineProperty(target, key,{
      })
    }
    // 3、方法装饰器 - target: Object, propertyKey:string, descriptor: TypePropertyDescript
    @decorator1
    class Course {
        constructor(){
          // 业务逻辑 
        }
        @propsWrapper
        public name:string;
         
        @methodDec
     }
上一篇下一篇

猜你喜欢

热点阅读