- 作者:老汪软件技巧
- 发表时间:2024-11-08 00:02
- 浏览量:
由于上周比较忙,其实这篇文章在上周已经完成了80%了,迟迟没有补充,所以现在才发出来。上期是讲到了TS日常类型中函数中的一些类型注解,这期继续后面的内容。
正文对象类型
在前面的基础上,我们知道了如何给变量声明类型,其实对象类型也是差不多的,但是要注意,给对象定义类型并不是定义整体的类型,而是要给每个属性都定义好类型。定义好后,对象的元素也只能有类型中的那几个。
比如:
const obj:{name:string,age:number} = {
name: "Bob",
age: 19
}
const obj2:{name:string,age:number} = {
name:"shang",
age: 10,
sex: "boy
} // 这种写法就是错误的,对象的属性里不能包含sex,因为在类型中没有定义sex
不仅不能多,也不能少
说到这,其实有办法可以多也可以少,就是可选属性和任意属性
可选属性和任意属性
可选属性
可选属性就是可以选择加上这个属性,也可以不要这个属性,语法就是在这个属性后面加上一个问号就行了
比如:
let xm:{name:string, age:number, height?:number} = {
name: "zhangsan",
age: 19
}
let xh:{name:string, age:number, height?:number} = {
name: "xh",
age: 19,
height: 178
}
上面两种写法都是正确的
任意属性
let obj:{
name:string,
[propName:string]:any
} = {
name: "xm",
age: 18
}
任意属性就是在类型约束中加上[propName:string]:type,这样的话就可以在对象中随意加上想要的属性了。
注意:propName 只是个形参,你可以叫任意名字,一般后面的type需要包含你已有的类型还要有你想要加的属性的类型,比如也可以这样写
let obj:{
name:string,
[propName:string]:string|number
} = {
name: "xm",
age: 18
}
这样写会让你的obj更准确,更不容易在运行时出错
联合类型定义联合类型
你可能会看到的第一种组合类型的方法是联合类型。联合类型是由两种或多种其他类型组成的类型,表示可能是这些类型中的任何一种的值。我们将这些类型中的每一种都称为联合的成员。
在类型中间加上|就是联合类型,表示值可以是这两种类型的其中一种,当然可以随意切换
let val:number|string = 1
val = "1"
使用联合类型
在使用联合类型的时候,需要注意几个地方
我来举个例子吧:
function fn(a:number|string) {
a.substring()
//报错
}
上面这种情况呢就是,ts编译器不能确认a的类型,所以不能使用只有在string类型有的函数,因为它可能是number但是可以使用两个类型都有的方法,比如toString()
还有一种解决方案就是缩小联合 ,使用typeof
function fn(a:number|string) {
if(typeof a == "string") {
a.substring()
} else if(typeof a == 'number') {
a.toFixed(2)
}
}
类型别名
我们一直通过直接在类型注释中编写对象类型和联合类型来使用它们。这很方便,但通常希望多次使用同一个类型并用一个名称引用它。
类型别名正是这样的 - 任何类型的名称。类型别名的语法是:
type = 类型
你可以使用类型别名来为任何类型命名,其实就是相当于给类型声明一个变量名
type MyNum = number
type MyStr = string
// 对象
type MyObj = {
name: string
}
// 函数
type MyFn = () => number
// 数组
type MyArr = number[]
// 元组
type MyCu = [number, string]
// 直接当成类型来使用就可以了
let num:myNum = 1
let obj:MyObj = {name:"xm"}
……
接口
关于接口,相信学过java的人并不陌生
接口声明是命名对象类型的另一种方式
interface MyObj{
name:string,
age:number
}
let obj:MyObj = {
name:"zhangsan",
age:19
}
接口的继承
可以使用extends关键字继承已有的接口
比如,一些共有的属性可以定义在一个接口中,然后在别的接口定义特殊的属性
interface Animal {
name:string,
age:number
}
interface Dog extends Animal {
run:() => void
}
interface Fish extends Animal {
swim:() => void
}
注意:这样实现后,在对象中包括继承而来的属性全部要有
const dog:Dog = {
name: "旺财",
age: 2,
run: () => console.log("跑步")
}
同名接口会自动合并
当自己声明了两个相同名字的接口时,它会自动合并。也可以认为是给第一次接口的声明添加属性。
interface Person {
name:string
}
interface Person {
age:number
}
const xm:Person = {
name:"xiaoming",
age: 19
}
上面的接口会合并为
interface Person{
name:string,
age:number
}
类型别名和接口的区别
一、定义方式和语法
interface(接口)
二、扩展方式
interface
三、实现和使用场景差异
interface
四、重复定义的处理
interface类型断言
有时候TS编译器无法确认值类型的信息比如:
function fn(a:number|string) {
a.substring()
}
这种情况的话使用类型断言可以让ts编译器不要检测这行代码就比如,下面这段代码不会报错,但是在运行时会报错
function fn(a:number|string) {
(a as string).substring()
}
fn(1)
注意:类型断⾔只能欺骗ts编译器,让他不报错,⽆法避免项⽬运⾏时的错误,所以使⽤断⾔要谨慎
在类型断言里有个值得注意的点:any类型可以断言为任意类型,任意类型也可以断言为any类型
// 任意类型断言为any类型
let num:number = 1
(num as any).length
// any类型断言为任意类型
let num:any = 5
console.log((num as number).length) //报错
非空断言
TypeScript 还具有一种特殊的语法,可以在不进行任何显式检查的情况下从类型中删除null和undefined。在任何表达式之后写!实际上是一个类型断言,该值不是null或undefined
type func = () => void
function fn(foo:func|null):void {
let bar = foo!() // 把null这个类型去除
let bar2 = foo() // 报错
}
双重断言(不推荐使用)
interface Girl{
name:string,
cost:()=>void
}
interface Boy{
name:string,
make:()=>void
}
function fn(obj:Girl){
obj as any as Boy
}
不推荐使用,这里我就不过多描述了
元组类型
元组类型其实就是在数组中含有不同类型元素的数组
const c:[number, string] = [1, 'string']
如果想要添加值,添加的值必须是这两个的联合类型(number|string)
type cu = [number,string]
const c:cu = [1,'1']
c.push('1') // ok
c.push(2) // ok
c.push(true) // 报错