1.TS 有什么优点和缺点?适应场景
2.TS 的数据类型有哪些?
3.TS 中 any 、void 、never、 unknwon 的区别?
4. TS 访问修饰符有哪几个?
5. # 和 private 定义的私有属性有什么区别?
6.TS中 type 和 interface有什么区别?如何选择?
7.说说你对TS中泛型的理解?
优点
-
-
- TS是静态类型
- TS有类型错误检查,而且是在编译时就报错(而非运行时)
- TS有智能提示,提高开发效率和稳定性
-
缺点
-
-
- 有一定学习成本
- 某些情况,类型定义过于混乱,可读性差
-
适应场景
-
-
- 大型项目,业务复杂,维护人员多
- 逻辑性比较强的代码,需要类型更稳固
- 组内至少有一个懂TS 的技术leader负责来把控代码规范
-
-
- boolean (布尔类型)
- number (数字类型)
- string (字符串类型)
- symbol
- undefined (未定义)
- 只能赋值本身和void类型
let c: undefined = undefined; c = void(0); c = undefined;
- 只能赋值本身和void类型
- null (空值,无值)
- 只能复制本身
let value:null = null; value = null
- 只能复制本身
- void (没有类型)
- 配合函数使用,表示该方法没有返回值
function hello():void{alert('hello')
// 或者返回 undefined
// return undefined }
- 配合函数使用,表示该方法没有返回值
- any (任意类型)
- 使用any类型时,不会做类型检查,甚至可以调用起属性和方法。和JS一样。
let num:any = 123 num = '24' num = true num.a
- 定义存储各种类型数据的数组时
let arrList: any[] = [1,false,'34'] arrList[1] = 100
- 使用any类型时,不会做类型检查,甚至可以调用起属性和方法。和JS一样。
- never (永远不存在的类型)
- 一般用在总会抛出异常或者无限循环中
// 死循环,无限循环 function f(): never{while(true){} }// 抛出异常 // 返回 never 的函数必须存在无法到达的终点 function err(message: string): never{throw new Error(message) }
- 一般用在总会抛出异常或者无限循环中
- unknown (未知类型)
- 当不确定变量的具体类型时,可以使用unknown
- unkwon 是安全的 any类型
- unknwon 需要通过类型断言或类型检查才能进行操作
let b: unknown; b = true; b = '123';console.log(b.length) // 错误 console.log((b as string).length) //需要断言
- array(数组类型)
- 数组array 是单一类型的
- 两种写法:
- 类型后面 + []
let arr:string[] = ['1']; arr = ['2','3']
- 使用数组泛型,Array<类型>
let arr:Array<number>; arr = [1,2]
- 类型后面 + []
- tuple (元组类型)
- 已知元素数量和类型,各个元素的类型可以不相同
- 赋值时,类型、位置、个数需要都要和定义的一直
let tupleArr:[number,string,boolean]; tupleArr = [12,'34',true] // 0k tupleArr = [12,'34'] // 错误的
- enum (枚举类型)
- 对JS 标准数据类型的一个补充
enum Color {Red,Green,Blue }let c: Color = Color.Red
- 对JS 标准数据类型的一个补充
- object (对象类型)
let obj:object obj = {name:'',age:10}
写法:变量名后加‘:’ 加上类型 的形式
const s:sting = 'a'
let n:number;
n = 100
TS 可以进行类型断言,如果变量直接赋值的情况,可以去掉类型(TS 自己会根据值去判断类型):即
const s = 'a'
3、TS 中 any 、void 、never、 unknwon 的区别?
- any 是任意类型,不做类型检查,比较危险
- unknown 是未知类型,和any 类似,但比any更加安全。因为unknown 会进行类型检查,使用时需要使用as断言
- void 是没有类型,用在一个函数的返回值,比如一个函数没有返回值,或者返回undefined
- never 是永远不存的类型,用在函数返回,比如死循环 或者 抛出异常错误的函数,该函数永远也无法达到终点
- public (公共)
- 可以自由的访问类程序里通过public定义的属性和方法。
- private (私有)
- private 定义的属性和方法,只能够在该类中的内部进行访问。继承的子类和实例对象不能访问到
- protected (受保护)
- 通过protected 定义的属性和方法 ,可以在该类内部和继承的子类中访问。但不能被实例对象访问
class Parent {public name:stringprivate friend: string = 'lili'protected age:numberconstructor (name: string, age: number){this.name = name;this.age = age}public hello(){}private hi(){}protected getName(){} } class Child extends Parent {constructor(name: string ,age: number){super(name,age)}getInfo(){console.log(child1.name) //公共的方法,可以被继承类访问console.log(this.hello()) //公共的方法,可以被继承类访问console.log(this.age)//受保护的属性,可以被继承类访问console.log(this.getName()) //受保护的方法,可以被继承类访问console.log(this.hi()) //私有方法 不能不继承类访问console.log(this.friend) //私有属性 不能被继承类访问 }} const child1 = new Child('xioaming',20) console.log(child1.name) //公共的方法,可以被实例对象访问 console.log(child1.hello()) //公共的方法,可以实例对象访问 console.log(child1.age)//受保护的属性,可以实例对象访问 console.log(child1.getName()) //受保护的方法,可以被实例对象访问 console.log(child1.hi()) //私有方法 不能被实例对象访问 console.log(child1.friend) //私有属性 不能被实例对象访问
TS中可以通过构造函数的参数直接定义属性:
class Parent {constructor(public name:string,protected age:number, private friend = 'lili'){} }//等同于下面的代码 class Parent {public name:stringprivate friend: string = 'lili'protected age:numberconstructor (name: string, age: number){this.name = name;this.age = age} }
面向对象的三要素: 1.继承 2. 封装 (保证私有属性不被访问)3. 多态(函数重载)
- # 和 private 在类中都是定义私有属性
class Parent {private name:string // 私有属性#age:number // 私有属性 constructor(name:string,age:number){this.name = namethis.#age = age}getInfo(){console.log(this.name) //获取私有属性console.log(this.#age) //获取私有属性} }
- #定义的属性,不能再构造函数参数中定义;而private可以在构造函数参数中直接定义
class Parent {#age:number // 私有属性 constructor(private name:string,age:number){this.#age = age}getInfo(){console.log(this.name)console.log(this.#age)} }
- private定义的私有属性,只是在编译时检查会给出警告,外部代码可以通过一些编译警告来访问这些成员(eg: 通过 as any进行断言 )。但在编译后的JS代码中,这些成员实际上是公开的。
- #定义的私有属性是真正的私有属性,只能在类的内部访问。外部代码无法访问这些成员。即使编译后的JavaScript代码仍也无法通过常规方式访问到
- # 适用于需要严格保护类成员不被访问的场景,确保封装性和安全性
- private 使用于在编译时进行检查,但不需要严格运行时进行隔离的场景。
6、TS中 type 和 interface有什么区别?如何选择?
- 类型别名 type:
- 类型别名:是用来给一个类型起个新名字的
- type可以支持多种类型定义:包括基本类型、对象类型、联合类型、交叉类型、元组等等
type Name = string type List = Array<number>
//对象 type UserType ={name: stringage: numbergetName: ()=> string }
// 函数
type SetName = (name: string)=> void
接口 interface:
-
- 接口interface:是一种用来描述对象或函数的东西
// 对象
interface User {name: stringage: numbergetName: ()=> string }
// 函数
interface setName {
(name:string):void
}
- 接口interface:是一种用来描述对象或函数的东西
二者的相同点:
-
- 都可以描述一个对象结构
- 都可以被 类class来实现接口,必须使用 implements 关键字
//interface 定义的类型
interface User {name: stringage: numbergetName: ()=> string}//type 定义的类型type UserType ={name: stringage: numbergetName: ()=> string}// Person 类实现了 User 接口class Person implements User{name: string;age: numbergetName: () => string } // Person 类实现了 UserType 接口 class Person implements UserType{name: string;age: numbergetName: () => string }
- 都可以扩展属性
- interface 扩展interface
interface User {name: stringage: number }interface School {schoolName:stringschoolDress:string }// User2 接口 扩展了 User 和 School interface User2 extends User,School{salary:number }const user: User2= {name: '23',age:20,schoolName:'ww',schoolDress:'xxxxx',salary: 30 }
- interface 扩展 type
type User = {name: stringage: number }//User 接口 扩展了User interface User2 extends User{salary:number }const user: User2= {name: '23',age:20,salary: 30 }
- type 扩展type
type User = {name: stringage: number }type School = {schoolName:stringschoolDress:string } // User2 类型 扩展 // type 类型的扩展使用交叉类型(&) type User2 = User & School & {salary:number }const user: User2= {name: '23',age:20,schoolName:'x',schoolDress:'xxx',salary: 30 }
- type 扩展 interface
interface User {name: stringage: number }type User2 = User & {salary:number }const user: User2= {name: '23',age:20,salary: 30 }
- interface 扩展interface
区 别:
-
- type 可以是基础类型、联合类型、交叉类型;interface 不可以
- typeof 可以通过typeof复制;interface 不可以
- interface 可以合并声明,type不可以重复定义
interface User {name: string }interface User {age: number }// interface 会合并声明 const user: User= {name: '23',age:20 }
//type 不能重复定义 type User = {name:string } // 会报标识符 User 重复 type User = {age:number }
type 和 interface 如何选择?
-
- TS的初衷:type 定义类型关系;interface 定义数据结构
- 但实际使用时,我们很多时候模糊不清
- 个人建议:优先使用 interface,再使用type
7.说说你对TS中泛型的理解?
是什么?
-
- 泛型指定义函数、类或接口时,不预先定义好具体类型,而是使用类型参数作为占位符
- 这些占位符在函数、类或者接口被实例化或调用时,再被指定类型
泛型的具体应用场景:
-
- 函数
- 简单例子:
function fn<T>(arg: T): T{return arg } fn<string>('hello') fn<number>(100)
-
可以一次定义多个类型参数
function fn3<T,U>(tupe:[T,U]):[U,T]{return [tupe[1],tupe[0]] } fn3<number,string>([100,'23']) fn3<boolean,string>([true,'23'])
- 简单例子:
- 类
- 基本使用
class Person<T> {name:Tconstructor(name:T){this.name = name}getName(): T {return this.name} } new Person('xxxx') new Person(12)
- 可以使用<T extends xx> 方式约束泛型
type Params = string | number class Person<T extends Params> {name:Tconstructor(name:T){this.name = name}getName(): T {return this.name} }
- 基本使用
- 接口
-
interface User<T,U> {id: Tname: stringage: numbersex: U } const u: User<string,number> = {id: '1',name: '33',age: 24,sex: 0 } const u2: User<string,string> = {id: '1',name: '33',age: 24,sex: '女' }
-
- 函数