2.1-TS的数据类型
Create by fall on 2021-02-15 Recently revised in 2022-04-19
简单类型的声明
Boolean
// 布尔类型
let flag:boolean = false
Number
和 JS 相同,TS 里面所有数字都是浮点数,都是 number
// 数字类型
let a1:number = 10 //十进制
let a2:number = 0b1010 //二进制
let a3:number = 0o12 // 八进制
let a4:number = 0xa // 十六进制
// 字符串类型
let str:string = '卷起千堆雪'
undefined & null
undefined 和 null可以作为其他数据类型的子类型(其他类型的子集),可以把该类型赋值给其他类型。
let notNum:undefined = undefined
let num1:number = null
let num2:number = undefined
Array
简单的声明
let arr1:number[] =[10,20,30,40]
let arr2:Array<number> = [100,200,300]
特殊类型
any 类型
个数不确定,或者类型不确定都可以使用 any
进行定义
// 任意数据类型
let num1:any = '遇贵人先立业,遇佳人先成家,遇富婆成家立业'
let arr:any[]= [11,35,'年少不知软饭香,错把青春到插秧']
变量在声明的时候,没有初始化,并且没有指定类型,就会被识别为 any
解决 any
带来的问题,TypeScript 3.0 引入了 unknown
类型。
unknown
同 any
,任何类型都可以赋值给 unknown
,但是 unknown
不能赋值给除 any
外其他类型(除非使用断言)。
let cheat:unknown ='wo'
let lier:string = cheat
也就是说,如果不缩小类型,就不能进行任何操作,必须使用类型断言,或者是使用其他缩小类型的方法。
function getDogName() {
let x: unknown;
return x;
};
const dogName = getDogName();
// 直接使用
// const upName = dogName.toLowerCase(); // Error
// typeof
if (typeof dogName === 'string') {
const upName:string = dogName.toLowerCase(); // OK
}
// 类型断言
const upName = (dogName as string).toLowerCase(); // OK
void 类型
没有任何类型,在方法后面使用,意味着该函数没有返回值
function show():void{
console.log('富婆把我心儿碎,予我别墅和崩溃')
}
方法没有返回值时,我们需要定义成
void
而不是undefined
否则会报错:声明类型不为
void
或any
的函数必须返回值。
object 类型
表示除了 number, string, boolean 之外的数据类型
function getObj(obj:object):object{
console.log(obj)
return {
name:'菲菲小公主',
age:62
}
}
console.log(getObj({giao:"giao"}))
never
表示一些永远不存在值的类型,有两种情况
- 执行时抛出异常
- 死循环代码,无限循环
never 是任何类型的子类型,也可以赋值给任何类型
function err(msg:string):never{
throw new Error(msg)
}
const err1 = (()=>{while(1){}})()
使用 never 避免出现新增了联合类型没有对应的实现,目的就是写出类型绝对安全的代码。
字面量类型
表示,子类型,比如
let str:'specific type' = 'specific type'
// 这段赋值表示:'specific type' 是 string 的子类型,而只能赋值为 string 的子类型 'specific type'
// 除此之外支持的字面量类型有
let num:666 = 666
let judge:true = true
字符串
单独的字面量类型没有多大意义,通过联合类型对数据进行限定
type Direction = 'forward' | 'backward'
function move():Direction{
if(Math.random()>0.5){
return 'forward'
}else{
return 'backward'
}
}
数字和布尔
interface State{
size:'big'|'middle'|'small'
edit:true|false
width: 120|180|240|360
}
元组 Tuple
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为 string
和 number
类型的元组。
元组最重要的意义是限制数组元素的个数和类型。
let arr:[string,number,boolean,] = ['哈',233,true]
// 数组中每一个相应的类型都只能使用对应的方法
console.log(arr[0].substring(1)) // OK
// arr[1].substring(1) // Error, 'number' 不存在 'substring' 方法
// 解构赋值
let [param1,param2,param3]=arr
// 可选元素,可选元素后不能跟必填元素
let arr1:[number,boolean?] = [13]
// 剩余元素
let arr2:[string,...number[]]
// 只读元组
let arr3:readonly [number,string]=[996,520]
// 定义了 Combine 的类型
type Combine = number|number[]
const a:Combine = 996
const arr:Combine = [520,996]
枚举类型
枚举类型中,每一个数据都可以叫做元素,
可以为每一个元素进行编号,枚举中的数据值可以为中文,但是不推荐
enum Color{
cyan='#0ff',
magenta,
yellow
}
let color:Color = Color.red
console.log(Color.cyan+"---"+Color)
类型别名
详细可以查看 4.3 接口和类型别名
上面最后一个示例就用到了类型别名,使用 type 就可以声明类型的别名,懒惰是程序员的第一生产力嘛。
type Message = string|string[]
let handleString =(msg:Message)=>{
}
// 现在 Message 表示 string 类型,或是数组内为 string 的类型
接口
详细可以查看 4.3 接口和类型别名
用接口来表明定义对象的类型,必须按照所有已定义的类型进行存储,且不能有任何多余。
在实现接口时,使用 interface
进行定义对象的类型。
- 可以使用
readonly
限制接口,只读 - 也可以使用
?
进行更改接口的值,使接口可以传值也可以不传。
interface OnePerson{
readonly name:string // readonly 表示该内容只读
age:number
hobby:string
salary?:number // ? 表示该内容可选
}
let tutou:OnePerson = {
name:'琦玉',
age:33,
hobby:"兴趣使然的英雄"
}
tutou.age = 66
console.log(tutou)
接口实现函数
interface Hero{
(name:string,heroName:string):boolean
}
const tutou:Hero = function(name:string,heroName:string):boolean{
// search 作用:返回符合元素的下标,如果为 -1 说明字符串中不存在该数值
return name.search(heroName) > -1
}
console.log(tutou("秃头披风侠","秃头"))
接口实现类
一个类的类型可以通过接口完成约束,并且一个类可以被多个接口进行约束。
接口和接口之间可以继承,类和接口之间称为实现
每一个方法必须都要可以使用
// 定义一个接口
interface OneHero{
hero()
}
// 此时接口可以看做 Person 类的一个约束
class Person implements OneHero{
hero(){
console.log('只有内心强大的人,才有资格做英雄')
}
}
let jodge = new Person
jodge.hero()
// 被多个接口修饰
interface TheHero{
action() // TheHero 限制必须拥有 action 方法
}
class Person implements OneHero,TheHero {
hero(){
console.log("一颗执着的心")
}
action(){
console.log("坚定不移的行动")
}
}
let faker = new Person
faker.hero()
faker.action()
交叉和联合
联合类型
取值时可以为多种类型中的一种类型,方法的两个类型(或者多个)都是可选的
function toString2(x: number | string) : string {
return x.toString()
}
联合类型数组
let arr:(number|string)[] = [12,'333']
联合类型常用用法
let num:1|2 = 1
type Events = 'click'|'scroll'|'mousemove'
交叉类型
交叉类型
// 一般是这么是用的
type Useless = number & string
// 但是也没啥意义,所以用下面的方法
type Format = string|number|number[]
type InitType = number|object
type Unity = Format&InitType
// 对象的联合
type InteractionType = {id:number,name:string}&{age:number}
const obj:InteractionType={
id:11,
name:'unknown',
age:555
}
确定类型
TS 如何确定,推断,或者是用户证明这个类型是什么类型
类型推断
在 TypeScript 中,具有初始化值的变量,有默认值得函数参数,函数返回类型都可以通过上下文判断出来
let a ='555'
a = 555 // 不能将 number 赋值给 string
// 推断返回类型
function add(a:number,b:number){
return a+ b
}
let sum = add(10,11)
sum = 'dssd'
断言
类型断言,有时候,你会比计算机更知道自己要怎么做,而此时,就可以使用断言
let arrayNum:number[] = [23,15,55,45]
// find 用于返回第一个满足条件的内容
// 你知道程序一定会返回数字,但是 TS 本身只是静态推断,因此它会认为可能返回 undefined
let num:number = arrayNum.find(num => num > 2) // ts 会报错
let num:number = arrayNum.find(num => num > 2) as number // 断言,认定返回的是 number 类型,此时赋值会忽略 undefined 的情况。
断言语法
// 尖括号 语法
const str:any = 'let me see it'
let number:number = (<string>str).length
// as 语法
const str2:any = 'jie no no no'
let number2:number = (str as string).length
因为尖括号语法,会和 JSX 冲突,所以建议使用 as 进行断言
非空断言
let superposition:number|null|undefined
superposition.toString()
superposition!.toString() // !放在声明变量末尾表示非空
确定赋值断言
如果一个数,没有被附初始值,就对它进行计算,会报错,但有一些情况 TS 无法捕捉,可以通过该方法帮助 TS 进行识别
let x:number
handle()
console.log(x*3) // error
function handle(){x=999}
let x!:number // 进行确定赋值断言,表示该值会被明确的赋值
谨慎使用断言 如果你没有按约定添加属性,TypeScript 编译器并不会对断言发出警告,所以使用时,要小心谨慎
interface Foo {
bar: number;
bas: string;
}
const foo = {} as Foo; // 它根本就不会报错
const fuu:Foo = {} // 报错
类型保护
类型保护和几个关键字有关 typeof
、instanceof
、in
// typeof
function foo(x:string|number){
if(typeof x === 'string'){ // typescript 认定 x 的类型是 string
x.split('')
}
x.split('') // 此时 无法确定 x 的类型
}
// instanceof
interface Fee{
old:string
}
interface Cee{
young:number
}
function doStuff(man:Fee|Cee){
if(man instanceof Fee){
console.log(man.old) // OK
console.log(man.young) // Error
}
}
// in
interface A{
x:number
}
interface B{
y:number
}
function moveThem(dir:A|B){
if(x in dir){
// q:A
}else{
// q:B
}
}
字面量类型、定义的类型保护
type Foo = {
kind:'foo'
foo:number
}
type Bar = {
kind:'bar'
bar:string
}
function doStuff(arg:Foo|Bar){
if(arg.kind === 'foo'){
arg.foo // ok
arg.bar // Error
}else{
// 一定是 Bar
}
}
自定义类型保护
interface Foo {
foo: number;
common: string;
}
interface Bar {
bar: number;
common: string;
}
// 用户自己定义的类型保护!
function isFoo(arg: Foo | Bar): arg is Foo {
return (arg as Foo).foo !== undefined;
}
// 用户自己定义的类型保护使用用例:
function doStuff(arg: Foo | Bar) {
if (isFoo(arg)) {
console.log(arg.foo); // ok
console.log(arg.bar); // Error
} else {
console.log(arg.foo); // Error
console.log(arg.bar); // ok
}
}
doStuff({ foo: 123, common: '123' });
doStuff({ bar: 123, common: '123' });
类
注:这里只是最简单的使用,详情看下面的章节
类的基本使用
class SelfIntrduce {
name: string
age: number
hobby: string
constructor(
name: string = '小甜甜',
age: number = 19,
hobby: string = 'both'
) {
this.name = name
this.age = age
this.hobby = hobby
}
introduce() {
console.log(
`大家好,俺是${this.name},今年俺${this.age},俺喜欢${this.hobby}`
)
}
}
let liu = new SelfIntrduce('狗子', 3, '嘿嘿')
liu.introduce()
函数类型
注:这里只是最简单的使用,详情在 4.2 函数类型
函数类型的声明
function sum(x:number,y:number):number{
return x+y
}
let sum2:(x:number,y:number)=>number= (x,y) => x.toString()
泛型
详情请看 5.2 泛型
function random<T>(arg:T):T{
return arg
}
// 首先是 <T> 在调用函数时使用,T 将会被传入的类型取代。
random<string>('22')
random<number>(333)
// 就像传入参数一样,传入类型,然后调用类型
参考文章
作者 | 链接 |
---|---|
Jimmy_kiwi | https://juejin.cn/post/7018805943710253086 |
深入理解 TypeScript | https://jkchao.github.io/typescript-book-chinese/ |