TypeScript 基础类型
基础类型
基础类型: Boolean
、 Number
、 String
、 null
、 undefined
以及 ES6 的 Symbol 和 ES10 的 BigInt 。
boolean 布尔值
let isDone: boolean = false;
注意,使用构造函数 Boolean
创造的对象 不是 布尔值:
let createdBoolean: boolean = new Boolean(1)
//这样会报错 应为事实上 new Boolean() 返回的是一个 Boolean 对象
需要改成:
let createdBoolean: Boolean = new Boolean(1)
let booleand2: boolean = Boolean(1) // 可以通过函数返回布尔值
number 数字
和 JavaScript 一样,TypeScript 里的所有数字都是 浮点数 。
这些浮点数的类型是 number
。并支持 二进制 、 八进制 、 十进制 、 十六进制
let notANumber: number = NaN;//Nan
let infinityNumber: number = Infinity;//无穷大
let decLiteral: number = 6; // 十进制
let hexLiteral: number = 0xf00d; // 十六进制
let binaryLiteral: number = 0b1010; // 二进制
let octalLiteral: number = 0o744; // 八进制
string 字符串
- 可以使用双引号(
"
)或单引号('
)表示字符串。 - 还可以使用模版字符串,它可以定义多行文本和内嵌表达式。
let name: string = "bob";
name = "smith";
let name: string = `Gene`;
let age: number = 37;
let sentence: string = `Hello, my name is ${ name }.
I'll be ${ age + 1 } years old next month.`;
Null 和 Undefined
TypeScript 里, undefined
和 null
两者各自有自己的类型分别叫做 undefined
和 null
。
和 void
相似,它们的本身的类型用处不是很大:
// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;
symbol
symbol
类型的值是通过 Symbol
构造函数创建的。 传递参做为唯一标识,并且只支持 string
和 number
、 undefined
类型的参数
let sym1 = Symbol();
let sym2 = Symbol("key"); // 可选的字符串 key
let sym3 = Symbol(1); // 可选的数字 key
let sym4 = Symbol(undefined);
symbol 是不可改变且唯一的。
let sym2 = Symbol("key");
let sym3 = Symbol("key");
sym2 === sym3; // false, symbols 是唯一的
像字符串一样,symbol 也可以被用做对象属性的键
let sym = Symbol();
let obj = {
[sym]: "value"
};
console.log(obj[sym]); // "value"
symbol 也可以与 计算出的属性名 声明 相结合来 声明对象的属性 和 类成员
const getClassNameSymbol = Symbol();
class C {
[getClassNameSymbol](){
return "C";
}
}
let c = new C();
let className = c[getClassNameSymbol](); // "C"
使用 symbol 定义的属性,是不能通过如下方式遍历拿到的
for in 遍历
Object.keys 遍历
getOwnPropertyNames
JSON.stringfy
const symbol1 = Symbol('666')
const symbol2 = Symbol('777')
const obj1= {
[symbol1]: '小满',
[symbol2]: '二蛋',
age: 19,
sex: '女'
}
// 1 for in 遍历
for (const key in obj1) {
// 注意在 console 看 key,是不是没有遍历到 symbol1
console.log(key)
}
// 2 Object.keys 遍历
Object.keys(obj1)
console.log(Object.keys(obj1))
// 3 getOwnPropertyNames
console.log(Object.getOwnPropertyNames(obj1))
// 4 JSON.stringfy
console.log(JSON.stringify(obj1))
怎么拿到?
Object.getOwnPropertySymbols
Reflect.ownKeys
// 1 拿到具体的 symbol 属性,对象中有几个就会拿到几个
Object.getOwnPropertySymbols(obj1)
console.log(Object.getOwnPropertySymbols(obj1))
// 2 es6 的 Reflect 拿到对象的所有属性
Reflect.ownKeys(obj1)
console.log(Reflect.ownKeys(obj1))
Symbol.iterator
迭代器 和 生成器 for of
支持遍历大部分类型迭代器 arr
、 nodeList
、 argumetns
、 set
、 map
等
var arr = [1,2,3,4];
let iterator:Iterator<number> = arr[Symbol.iterator]();
console.log(iterator.next()); //{ value: 1, done: false }
console.log(iterator.next()); //{ value: 2, done: false }
console.log(iterator.next()); //{ value: 3, done: false }
console.log(iterator.next()); //{ value: 4, done: false }
console.log(iterator.next()); //{ value: undefined, done: true }
interface Item {
age: number,
name: string
}
const array: Array<Item> = [{ age: 123, name: "1" }, { age: 123, name: "2" }, { age: 123, name: "3" }]
type mapTypes = string | number
const map:Map<mapTypes,mapTypes> = new Map()
map.set('1','王爷')
map.set('2','陆北')
const obj = {
aaa:123,
bbb:456
}
let set:Set<number> = new Set([1,2,3,4,5,6])
// let it:Iterator<Item> = array[Symbol.iterator]()
const gen = (erg:any): void => {
let it: Iterator<any> = erg[Symbol.iterator]()
let next:any= { done: false } // 默认值
while (!next.done) {
next = it.next()
if (!next.done) {
console.log(next.value)
}
}
}
gen(array)
内置 symbols
用来表示语言内部的行为
1. Symbol.hasInstance
方法,会被 instanceof
运算符调用。构造器对象用来识别一个对象是否是其实例。
2. Symbol.isConcatSpreadable
布尔值,表示当在一个对象上调用 Array.prototype.concat
时,这个对象的数组元素是否可展开。
3. Symbol.iterator
方法,被 for of
语句调用。返回对象的默认迭代器。
4. Symbol.match
方法,被 String.prototype.match
调用。正则表达式用来匹配字符串。
5. Symbol.replace
方法,被 String.prototype.replace
调用。正则表达式用来替换字符串中匹配的子串。
6. Symbol.search
方法,被 String.prototype.search
调用。正则表达式返回被匹配部分在字符串中的索引。
7. Symbol.species
函数值,为一个构造函数。用来创建派生对象。
8. Symbol.split
方法,被 String.prototype.split
调用。正则表达式来用分割字符串。
9. Symbol.toPrimitive
方法,被 ToPrimitive
抽象操作调用。把对象转换为相应的原始值。
10. Symbol.toStringTag
方法,被内置方法 Object.prototype.toString
调用。返回创建对象时默认的字符串描述。
11. Symbol.unscopables
对象,它自己拥有的属性会被 with
作用域排除在外。
其他类型
Any
任意类型 和 Unknown
顶级类型
any
为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能 来自于动态的内容 ,比如来自 用户输入 或 第三方代码库 。
这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用 any
类型来标记这些变量(声明变量的时候没有指定任意类型也默认为 any
)
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
在对现有代码进行改写的时候, any
类型是十分有用的,它允许你 在编译时可选择地包含或移除类型检查 。
你可能认为 Object
有相似的作用,就像它在其它语言中那样。 但是 Object
类型的变量只是允许你给它赋任意值 - 但是却不能够在它上面调用任意的方法,即便它真的有这些方法 :
let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)
let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.
当你 只知道一部分数据的类型 时, any
类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据:
let list: any[] = [1, true, "free"];
list[1] = 100;
unknown
TypeScript 3.0 中引入的 unknown
类型也被认为是 top type ,但它更安全。与 any
一样,所有类型都可以分配给 unknown
unknow
类型比 any
更加严格当你要使用 any
的时候可以尝试使用 unknow
//unknown 可以定义任何类型的值
let value: unknown;
value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = null; // OK
value = undefined; // OK
value = Symbol("type"); // OK
any
可以作为父类型和子类型, unknow
类型只能作为父类型不能作为子类型(但可以赋值给 unknown
和 any
)
// 这样写会报错 unknow 类型只能作为父类型不能作为子类型,any 可以作为父类型和子类型
// unknown 类型不能赋值给其他类型
let names:unknown = '123'
let names2:string = names
// 这样就没问题 any 类型是可以的
let names:any = '123'
let names2:string = names
// unknown 可赋值对象只有 unknown 和 any
let bbb:unknown = '123'
let aaa:any= '456'
aaa = bbb
any
类型在对象没有这个属性的时候还在获取是不会报错的, unknow
是不能调用属性和方法是会报错
// 如果是 any 类型在对象没有这个属性的时候还在获取是不会报错的
let obj:any = {b:1} obj.a
// 如果是 unknow 是不能调用属性和方法,会报错
let obj:unknown = {b:1,ccc:():number=>213}
obj.b
obj.ccc()
Void 空值类型
void
类型像是与 any
类型相反,它表示 没有任何类型 。
当一个函数没有返回值时,通常会见到其返回值类型是 void
:
function warnUser(): void {
console.log("This is my warning message");
}
声明一个 void
类型的变量没有什么大用,因为只能为它赋予 undefined
和 null(只在--strictNullChecks 未指定时)
:
let unusable: void = undefined;
默认情况下 null
和 undefined
是所有类型的子类型。 即可以把 null
和 undefined
赋值给如 number
类型的变量,但 void
类型不可以分给其他类型。
//这样写会报错 void 类型不可以分给其他类型
let test: void = undefined
let num2: string = "1"
num2 = test
//这样是没问题的
let test: null = null
let num2: string = "1"
num2 = test
//或者这样的
let test: undefined = undefined
let num2: string = "1"
num2 = test
然而,当你 指定了 --strictNullChecks
标记 , null
和 undefined
只能赋值给 any
和它们各自的类型(有一个例外是 undefined
还可以赋值给 void
类型,但 null
不能)。
// tsconfig.json 开启了严格模式
{
"compilerOptions":{
"strict": true
}
}
这能避免很多常见的问题。 也许在某处你想传入一个 string
或 null
或 undefined
,你可以使用联合类型 string | null | undefined
。
never
类型
never
类型表示的是那些永不存在的值的类型。
never
类型是那些总是会 抛出异常 或 根本就 不会有返回值 的 函数表达式 或 箭头函数表达式 的返回值类型。
变量也可能是 never
类型,当它们被永不为真的类型保护所约束时 。
注意:never
类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是 never
的子类型或可以赋值给 never
类型(除了 never
本身之外)。 即使 any
也不可以赋值给 never
。
下面是一些返回 never
类型的函数:
// 返回 never 的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
// 推断的返回值类型为 never
function fail() {
return error("Something failed");
}
// 返回 never 的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}
应用场景
interface A {
type: "foo"
}
interface B {
type: "bar"
}
type All = A | B ;
function handleValue(val: All) {
switch (val.type) {
case 'foo':
break;
case 'bar':
break
default:
//兜底逻辑 一般是不会进入这儿如果进来了就是程序异常了
const exhaustiveCheck:never = val;
break
}
}
比如新来了一个同事他新增了一个 C 接口,我们必须手动找到所有 switch 代码并处理,否则将有可能引入 BUG 。
而且这将是一个“隐蔽型”的 BUG,如果回归面不够广,很难发现此类 BUG。
那么我们就需要 TS 在类型检查阶段发现这个问题
interface A {
type: "foo"
}
interface B {
type: "bar"
}
interface C {
type: "bizz"
}
type All = A | B | C;
function handleValue(val: All) {
switch (val.type) {
case 'foo':
break;
case 'bar':
break
default:
//兜底逻辑 一般是不会进入这儿如果进来了就是程序异常了
const exhaustiveCheck: never = val;
break
}
}
由于任何类型都不能赋值给 never
类型的变量,所以当存在进入 default
分支的可能性时,TS 的类型检查会及时帮我们发现这个问题
enum 枚举
数字枚举
使用枚举类型可以 为一组数值赋予友好的名字 。
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
默认情况下,从 0
开始为元素编号。 也可以手动的指定成员的数值。 例如,我们将上面的例子改成从 1
开始编号:
enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;
枚举类型提供的一个便利是你可以 由枚举的值得到它的名字 。 例如,我们知道数值为 2,但是不确定它映射到 Color 里的哪个名字,我们可以查找相应的名字:
enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];
console.log(colorName); // 显示'Green'因为上面代码里它的值是 2
字符串枚举
字符串枚举的概念很简单。 在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。
enum Types{
Red = 'red',
Green = 'green',
BLue = 'blue'
}
由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。 换句话说,如果你正在调试并且必须要读一个数字枚举的运行时的值,这个值通常是很难读的 - 它并不能表达有用的信息,字符串枚举允许你提供一个运行时有意义的并且可读的值,独立于枚举成员的名字。
异构枚举
枚举可以混合字符串和数字成员
enum Types{
No = "No",
Yes = 1,
}
编译为 js
var Types;
(function (Types) {
Types["No"] = "No";
Types[Types["Yes"] = 1] = "Yes";
})(Types || (Types = {}));
接口枚举
定义一个枚举 Types,定义一个接口 A,接口 A 有一个属性 red 值为 Types.yyds
声明对象的时候要遵循这个规则
enum Types {
yyds,
dddd
}
interface A {
red:Types.yyds
}
let obj:A = {
red:Types.yyds
}
const
常量枚举
let 和 var 都是不允许的,声明只能使用 const
, const
声明的枚举会被编译成常量。
常量枚举可以提升性能(可以提升性能,计算枚举不能用 const)
enum Direction {
Up = "Up",
Down = "Down",
Left = "Left",
Right = "Right"
}
// 常量枚举
const enum Direction2 {
Up = "Up",
Down = "Down",
Left = "Left",
Right = "Right"
}
if ("Up" === Direction2.Up) {
console.log('upupuup')
}
Array 数组
三种方式可以定义数组:
- 在元素类型后面接上
[]
,表示由此类型元素组成的一个数组let list: number[] = [1, 2, 3]; let listAny: any[] = [1, true, 'str']; let data:number[][] = [[1,2], [3,4]];
- 使用数组泛型
let list: Array<number> = [1, 2, 3]; let data: Array<Array<number>> = [[1,2,3], [4,5,6]]
- 用接口表示数组 :一般用来描述类数组
interface NumberArray { [index: number]: number; } let fibonacci: NumberArray = [1, 1, 2, 3, 5]; // 表示:只要索引的类型是数字时,那么值的类型必须是数字。
arguments 类数组
function Arr(...args:any): void {
console.log(arguments)
//错误的 arguments 是类数组不能这样定义
let arr:number[] = arguments
}
Arr(111, 222, 333)
function Arr(...args:any): void {
console.log(arguments)
//ts 内置对象 IArguments 定义
let arr:IArguments = arguments
}
Arr(111, 222, 333)
//其中 IArguments 是 TypeScript 中定义好了的类型,它实际上就是:
interface IArguments {
[index: number]: any;
length: number;
callee: Function;
}
Tuple 元组
元组就是数组的变种
元组(Tuple)是固定数量的不同类型的元素的组合的数组。
元组与集合的不同之处在于,元组中的元素类型可以是不同的,而且数量固定。元组的好处在于可以把多个元素作为一个单元传递。如果一个方法需要返回多个值,可以把这多个值作为元组返回,而不需要创建额外的类来表示。
比如定义一对值分别为 string
和 number
类型的元组。
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
当访问一个越界的元素,会使用联合类型替代:
x[3] = 'world'; // OK, 字符串可以赋值给(string | number) 类型
console.log(x[5].toString()); // OK, 'string' 和 'number' 都有 toString
x[6] = true; // Error, 布尔不是(string | number) 类型
Object
object
表示非原始类型,也就是除 number
, string
, boolean
, symbol
, null
或 undefined
之外的类型。
使用 object
类型,就可以更好的表示像 Object.create
这样的 API。例如:
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK
create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error
类型断言(即类型转换)
有时候你会遇到这样的情况,你会比 TypeScript 更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。
通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换 ,但是不进行特殊的数据检查和解构。 它没有运行时的影响, 只是在编译阶段起作用 。 TypeScript 会假设你,程序员,已经进行了必须的检查。
类型断言有两种形式(两种形式是等价的):
- 尖括号语法
let someValue: any = "this is a string"; let strLength: number = (<string>someValue).length;
- as 语法 (在 TypeScript 里 使用 JSX 时 ,只有
as
语法断言是被允许):值 as 类型 value as stringlet someValue: any = "this is a string"; let strLength: number = (someValue as string).length;
注意: 「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误
使用 any 临时断言
window.abc = 123; // 这样写会报错因为 window 没有 abc 这个东西
(window as any).abc = 123 // 可以使用 any 临时断言在 any 类型的变量上,访问任何属性都是允许的。
as const
as
是对字面值的 断言 ,与 const
直接定义常量是有区别的
const names = '小满'
names = 'aa' //无法修改
let names2 = '小满' as const // 跟直接 const 声明是一样的
names2 = 'aa' //无法修改
// 数组
let a1 = [10, 20] as const;
const a2 = [10, 20];
a1.unshift(30); // 错误,此时已经断言字面量为[10, 20],数据无法做任何修改
a2.unshift(30); // 通过,没有修改指针
类型断言是不具影响力的
在下面的例子中,将 something 断言为 boolean 虽然可以通过编译,但是并没有什么用 并不会影响结果,因为编译过程中会删除类型断言
function toBoolean(something: any): boolean {
return something as boolean;
}
toBoolean(1);
// 返回值为 1
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论