一、TS面试基础知识
1、什么是TS?
-
概念:
ts 是 js 的超集,即在原有的js语法基础上,添加强类型并切换为基于类的面向对象语言 - 特点
面向项目:
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 基础类型和语法
- 类型: boolean 、string 、number 、arrary、 null 、undefined
// es
let a = false;
// ts
let a:boolean = false;
// 统一方式 & <> 方式
let classArr: string[] = ['1', '2'];
let classArr: Array<string> = ['1', '2']
- 那如果数组中的各个元素类型不一样的话呢?
用tuple - 元祖
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 = {})
- 那我想绕过所有的类型检查呢 ==> any 类型检查和编译筛查取消
let anyValue:any = '1';
anyValue = false;
let value1:boolean = anyValue // OK 使用前无需判断any类型
- 还有一个种方式是 ==> unknown 绕过赋值检查,禁止更改传递
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')
}
- 永不能执行完 or 永远error (永远不会有值)
// 比如专门用来报错的函数
function errMsg(msg:string):never{
throw new Error(msg)
}
// 或者永不会结束的函数
function infiniteLoop:never{
while(true) { }
}
4、接口 - interface
- 区分开object / Object / {} - 三个不同的对象
// 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 - 节点、元素
- 装饰器 - decorator 用处很大,通用性的操作
// 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
}