一、枚举类型
使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。 TypeScript支持数字的和基于字符串的枚举。
枚举类型的特点
- 可以给一组数值取上一个更好理解的名字;
- 一个枚举中只会存在几个固定的值,并不会出现超出范围的可能性;
在很多编程语言中都会有枚举这种数据结构,但是在 JavaScript 中没有这种枚举数据结构,很多时候都是用一个对象来模拟枚举,如下:
在 Typescript 当中可以用 enum 关键词声明一个枚举,{ } 里面是枚举的值,注意的是用 = 号,使用方式跟对象一样。const PostStatus = { name: '迪西', age: 9, className: '三年级' }
enum PostStatus { name = '迪西', age = 9, className = '三年级' }
- 数字枚举
枚举值自动基于前一个值自增,如果没指定具体数值,则从 0 开始。enum PostStatus1 { Draft, Draft1 = 5, Draft2 = 7, Draft3 } console.log(PostStatus1.Draft) // 0 console.log(PostStatus1.Draft1) // 5 console.log(PostStatus1.Draft2) // 7 console.log(PostStatus1.Draft3) // 8
- 字符串枚举
字符串枚举无法自增,需要手动添加,字符串枚举不太常见, 如果不添加会报错。enum PostStatus1 { Str = 'a', Str1 = 'b', Str2 = 'c', Str3 = 'd', } console.log(PostStatus1.Str) // a console.log(PostStatus1.Str1) // b console.log(PostStatus1.Str2) // c console.log(PostStatus1.Str3) // d
- 计算的和常量成员
每个枚举成员都带有一个值,它可以是 常量或 计算出来的。 当满足如下条件时,枚举成员被当作是常量:- 它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值 0:
enum PostStatus1 { Draft, } console.log(PostStatus1.Draft) // 0
- 它不带有初始化器且它之前的枚举成员是一个 数字常量。 这种情况下,当前枚举成员的值为它上一个枚举成员的值加1。
enum PostStatus1 { Draft = 1, Draft1, Draft2, } console.log(PostStatus1.Draft) // 1 console.log(PostStatus1.Draft1) // 2 console.log(PostStatus1.Draft2) // 3
- 枚举成员使用 常量枚举表达式初始化。 常数枚举表达式是TypeScript表达式的子集,它可以在编译阶段求值。 当一个表达式满足下面条件之一时,它就是一个常量枚举表达式:
①: 一个枚举表达式字面量(主要是字符串字面量或数字字面量)
②: 一个对之前定义的常量枚举成员的引用(可以是在不同的枚举类型中定义的)
③: 带括号的常量枚举表达式
④: 一元运算符 +, -, ~其中之一应用在了常量枚举表达式
⑤: 常量枚举表达式做为二元运算符 +, -, *, /, %, <<, >>, >>>, &, |, ^的操作对象。 若常数枚举表达式求值后为 NaN或 Infinity,则会在编译阶段报错。
所有其它情况的枚举成员被当作是需要计算得出的值。enum FileAccess { None, Read = 1 << 1, Write = 1 << 2, ReadWrite = Read | Write, G = "123".length } console.log(FileAccess.None) // 0 console.log(FileAccess.Read) // 2 console.log(FileAccess.Write) // 4 console.log(FileAccess.ReadWrite) // 6 console.log(FileAccess.G) // 3
- 常量枚举
如果我们确认我们代码中不会使用索引器的方式去访问枚举,那推荐使用常量枚举,常量枚举:enum 前面加个关键词 const。
常量枚举只能使用常量枚举表达式,并且不同于常规的枚举,它们在编译阶段会被删除。 常量枚举成员在使用的地方会被内联进来。 之所以可以这么做是因为,常量枚举不允许包含计算成员。const enum Enum { A = 1, B = A * 2 }
编译前
编译后const enum Directions { Up, Down, Left, Right } let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]
var directions = [0 /* Directions.Up */, 1 /* Directions.Down */, 2 /* Directions.Left */, 3 /* Directions.Right */];
- 枚举类型会影响编译结果
枚举类型会入侵到运行时的代码,也就是说它会影响编译后的结果,会编译成一个双向的键值对对象,目的是可以让我们动态的根据枚举值去或者枚举的名称,也就是说可以通过索引器的方式去访问对应的枚举名称。
编译前
编译后enum Directions { Up, Down, Left, Right } const post = { title: 'Hello TypeScript', content: 'TypeScript is a typed superset of JavaScript.', positionUp: Directions.Up }
var Directions; (function (Directions) { Directions[Directions["Up"] = 0] = "Up"; Directions[Directions["Down"] = 1] = "Down"; Directions[Directions["Left"] = 2] = "Left"; Directions[Directions["Right"] = 3] = "Right"; })(Directions || (Directions = {})); var post = { title: 'Hello TypeScript', content: 'TypeScript is a typed superset of JavaScript.', positionUp: Directions.Up };
二、Type 类型别名
Type又叫类型别名(type alias),作用是给一个类型起一个新名字,
不仅支持interface定义的对象结构,还支持基本类型、联合类型、交叉类型、元组等任何你需要手写的类型。
type num = number; // 基本类型
type stringOrNum = string | number; // 联合类型
type person = {name: string}; // 对象类型
type user = person & { age: number } // 交叉类型
type data = [string, number]; // 元组
type fun = () => void; // 函数类型
- type和interface的相同点:
- 都可以用来描述一个对象或者函数
interface
typeinterface user {name: string; age:number}; // 对象 interface setUser {(name: string; age:number):void}; // 函数
type user = {name: string; age:number}; // 对象 type setUser = (name: string; age:number):void;//函数
- 都可以进行拓展
interface可以扩展,
type可以通过交叉实现interface的extends行为interface userName { name: string; } interface user extends userName { age: number } let stu:user = {name: 'wang', age: 10}
interface可以extends type,type userName = { name: string; } type user = userName & {age: number} let stu:user = {name: 'wang', age: 18}
type也可以与interface类型交叉 。type name = { name: string; } interface user extends name { age: number; } let stu:user = {name: 'wang', age: 89}
interface name { name: string; } type user = name & { age: number; } let stu:user = {name:'wang', age: 18}
- 都可以用来描述一个对象或者函数
- type和interface的不同点:
- type可以做到而interface不能做到
type可以声明基本类型
type可以声明联合类型type name = string;
type可以声明元祖类型type name = string | number;
type可以通过typeof操作符来声明type infoList = [string, number];
type typeObj = typeof someObj;
- interface 可以做到而 type 不能做到
interface 可以声明合并。
如果是 type 的话,就会报重复定义的警告,因此是无法实现声明合并的。interface info { name: string } interface info { age: number } /* info 实际为 { name: string age: number } */
- type可以做到而interface不能做到
- 使用建议
- 推荐使用 interface,其他无法满足需求的情况下用 type。但是因为联合类型和交叉类型是比较常用的,所以避免不了大量使用 type 的场景,一些复杂类型也需要通过组装后形成类型别名来使用。
- 如果想保持代码统一,还是可选择使用 type。通过上面的对比,type 其实可涵盖 interface 的大部分场景。
- 对于 React 或Vue 组件中 props 及 state,推荐使用 type,这样能够保证使用组件的地方不能随意在上面添加属性。如果有自定义需求,可通过 HOC(高阶组件)二次封装。
- 编写三方库时使推荐使用 interface,其更加灵活自动的类型合并可应对未知的复杂使用场景。
三、运算符
运算符用于执行程序代码运算
-
算术运算符
算术运算符用于对数字进行基本的算术计算。
假设:x = 5运算符 描述 例子 x运算结果 y运算结果 + 加法(用于将两个数字相加。) y=x+5 5 10 - 减法(用于从一个数字中减去另一个数字。) y=x-2 5 3 * 乘法(用于将两个数字相乘。) y=x*2 5 10 / 除法(用于将一个数字除以另一个数字。) y=15/x 5 3 % 取模(余数)(用于计算一个数字除以另一个数字后的余数。) y=x%2 5 1 ++ 自增 (用于将一个数字增加 1。) y = x++ 5 5 ++ 自增 y= ++x 5 6 - - 自减(用于将一个数字减少 1) y= x– 5 5 - - 自减 y= --x 5 4 注:在进行自增(++)的运算时,如果运算符(++)放在操作数的前面是先进行自增运算,再进行其他运算。
若是运算符放在操作数的后面则是先进行其他运算再进行自增运算。同理可知自减(–)运算亦是如此。// 加法 // const x:number = 5; // let y:number = x + 5 // console.log(y) // 10 // 减法 // const x:number = 5; // let y:number = x - 2 // console.log(y) // 3 // 乘法 // const x:number = 5; // let y:number = x * 2 // console.log(y) // 10 // 除法 // const x:number = 5; // let y:number = 15 / x // console.log(y) // 3 // 取模(余数) // const x:number = 5; // let y:number = x%2 // console.log(y) // 1 // 自增 ++在后 // let x:number = 5; // let y:number = x++; // console.log(y) // 5 // 自增 ++在前 // let x:number = 5; // let y:number = ++x; // console.log(y) // 6 //两个++联用 // let x:number = 5; // let y:number = x++; // y = ++x; // console.log(y) // 7 // let x:number = 5; // let y:number = ++x; // y = x++; // console.log(y) // 6 // -- 在后 // let x:number = 5; // let y:number = x--; // console.log(y) // 5 // -- 在前 // let x:number = 5; // let y:number = --x; // console.log(y) // 4
-
逻辑运算符
逻辑运算符用于测定变量或值之间的逻辑 并生成布尔值结果。
给定 x=6 以及 y=3,下表解释了逻辑运算符:运算符 描述 例子 &&(并且) 用于在两个条件都为真时返回真。 (x < 10 && y > 1)为 true 2个竖线(这里表达不出来)(或者 ) 用于在至少一个条件为真时返回真。 (x5 或者 y5) 为 false ! (非) 用于对表达式取反。 !(x==y) 为 true // && 并且 // let a: number = 10; // let b: number = 6; // let result: boolean = a > 8 && b > 5; // console.log(result) // true // || 或者 // let a: number = 10; // let b: number = 6; // let result: boolean = a > 8 || b > 7; // a大于8或者b大于7 有一个就返回true // console.log(result) // true // !非 // let a: number = 10; // let b: number = 6; // let result: boolean = !(a == b); // console.log(result) // true
-
关系运算符
关系运算符用于计算结果是否为 true 或者 false。运算符 描述 比较 返回值 ==(相等) 用于比较两个值是否相等。 (5 == 8) false !=(不等) 用于比较两个值是否不相等。 (5 != 8) true > (大于) 用于判断左边的值是否大于右边的值。 (5 > 8) false < (小于) 用于判断左边的值是否小于右边的值。 (5 < 8) true >=(大于等于) 用于判断左边的值是否大于等于右边的值。 (5 >= 8) false <= (小于等于) 用于判断左边的值是否小于等于右边的值。 (5 <= 8) true var num1:number = 5; var num2:number = 9; console.log("num1 的值为: "+num1); // num1 的值为: 5 console.log("num2 的值为:"+num2); // num2 的值为:9 var res = num1>num2 console.log("num1 大于n num2: "+res) // num1 大于n num2: false res = num1<num2 console.log("num1 小于 num2: "+res) // num1 小于 num2: true res = num1>=num2 console.log("num1 大于或等于 num2: "+res) // num1 大于或等于 num2: false res = num1<=num2 console.log("num1 小于或等于 num2: "+res) // num1 小于或等于 num2: true res = num1==num2 console.log("num1 等于 num2: "+res) // num1 等于 num2: false res = num1!=num2 console.log("num1 不等于 num2: "+res) // num1 不等于 num2: true
-
位运算符
位操作是程序设计中对位模式按位或二进制数的一元和二元操作。运算符 描述 例子 类似于 结果 十进制 & AND,按位与处理两个长度相同的二进制数,两个相应的二进位都为 1,该位的结果值才为 1,否则为 0。 x = 5 & 1 0101 & 0001 0001 1 竖线 OR,按位或处理两个长度相同的二进制数,两个相应的二进位中只要有一个为 1,该位的结果值为 1。 x = 5 竖线 1 0101 & 0001 0101 1 ~ 取反,取反是一元运算符,对一个二进制数的每一位执行逻辑反操作。使数字 1 成为 0,0 成为 1。 x = ~ 5 ~0101 1010 -6 ^ 异或,按位异或运算,对等长二进制模式按位或二进制数的每一位执行逻辑异按位或操作。操作的结果是如果某位不同则该位为 1,否则该位为 0。 x = 5 ^ 1 0101 ^ 0001 0100 4 << 左移,把 << 左边的运算数的各二进位全部左移若干位,由 << 右边的数指定移动的位数,高位丢弃,低位补 0。 x = 5 << 1 0101 << 1 1010 10 >> 右移,把 >> 左边的运算数的各二进位全部右移若干位,>> 右边的数指定移动的位数。 x = 5 >> 1 0101 >> 1 0010 2 >>> 无符号右移,与有符号右移位类似,除了左边一律使用0 补位。 x = 2 >>> 1 0010 >>> 1 0001 1 var a:number = 2; // 二进制 10 var b:number = 3; // 二进制 11 var result; result = (a & b); console.log("(a & b) => ",result) // (a & b) => 2 result = (a | b); console.log("(a | b) => ",result) // (a | b) => 3 result = (a ^ b); console.log("(a ^ b) => ",result); // (a ^ b) => 1 result = (~b); console.log("(~b) => ",result); // (~b) => -4 result = (a << b); console.log("(a << b) => ",result); // (a << b) => 16 result = (a >> b); console.log("(a >> b) => ",result); // (a >> b) => 0 result = (a >>> 1); console.log("(a >>> 1) => ",result); // (a >>> 1) => 1
-
赋值运算符
赋值运算符用于给变量赋值。
给定 x=10 和 y=5,下面的表格解释了赋值运算符:运算符 描述 例子 范例 x值 =(赋值) 用于将右边的值赋给左边的变量 x = y x = y x=5 +=(先进行加运算后赋值) 用于将右边的值与左边的变量相加,并将结果赋给左边的变量。 x += y x = x + y x = 15 -=(先进行减运算后赋值) 用于将右边的值从左边的变量中减去,并将结果赋给左边的变量。 x -= y x = x - y x = 5 *=(先进行乘运算后赋值) 用于将右边的值与左边的变量相乘,并将结果赋给左边的变量。 x *= y x = x * y x = 50 /= (先进行除运算后赋值) 用于将左边的变量除以右边的值,并将结果赋给左边的变量。 x /= y x = x / y x = 2 类似的逻辑运算符也可以与赋值运算符联合使用:<<=, >>=, >>=, &=, |= 与 ^=。
var a: number = 12 var b:number = 10 a = b console.log("a = b: "+a) // a = b: 10 a += b console.log("a+=b: "+a) // a+=b: 20 a -= b console.log("a-=b: "+a) // a-=b: 10 a *= b console.log("a*=b: "+a) // a*=b: 100 a /= b console.log("a/=b: "+a) // a/=b: 10 a %= b console.log("a%=b: "+a) // a%=b: 0
-
三元/条件运算符
三元运算有 3 个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量。
Test ? expr1 : expr2Test - 指定的条件语句
expr1 - 如果条件语句 Test 返回 true 则返回该值
expr2 - 如果条件语句 Test 返回 false 则返回该值var num:number = -2 var result = num > 0 ? "大于 0" : "小于 0,或等于 0" console.log(result) // 小于 0,或等于 0
-
字符串运算符:连接运算符 (+)
运算符可以拼接两个字符串,var msg:string = "codebaoku"+".com" console.log(msg) // codebaoku.com
-
类型运算符
- typeof 运算符 typeof 是一元运算符,返回操作数的数据类型。
var num = 12 console.log(typeof num); // number
- instanceof 运算符检查一个对象是否是一个特定的构造函数创建的,或者是否实现了一个特定的接口。它只能用在表达式中,不能用在类型上下文中。它返回一个布尔值,表示对象是否属于构造函数的原型链。例如:
interface Animal { eat(): void; } class Dog implements Animal { eat() { console.log("Dog eats"); } bark() { console.log("Dog barks"); } } let dog = new Dog(); console.log(dog instanceof Dog); // true console.log(dog instanceof Animal); // true
typeof 和 instanceof 的使用场景也不同:
- 使用 typeof 来获取或检查简单的内置类型,例如字符串,数字,布尔值等。
- 使用 instanceof 来检查自定义的类型,例如类,接口等。
- 使用 instanceof 来检查复杂的内置类型,例如数组,正则表达式等。