# 基础类型
# 基本类型
基础类型: boolean
, number
, string
, symbol
, null
和undefined
对象类型: {}
或者 object
, []
或者 Array<any>
, 还有function, Class类型
注意:
- number和Number的区别:TS中指定类型的时候要用
number
,这个是TypeScript的类型关键字。而Number
为JavaScript的原生构造函数,用它来创建数值类型的值; - TypeScript 和 JavaScript 一样,所有数字都是浮点数,所以只有一个
number
类型,而没有int
或者float
类型; - TypeScript 还支持 ES6 中新增的二进制和八进制数字字面量,TypeScript 中共支持二、八、十和十六四种进制的数值;
- 默认情况下
undefined
和null
可以赋值给任意类型的值;当你在tsconfig.json
的"compilerOptions"
里设置了"strictNullChecks": true
时,那必须严格对待,undefined
和null
将只能赋值给它们自身和 void 类;TS 对可选属性和对可选参数的处理一样,会被自动加上|undefined
; object
是引用类型
一些基础类型的示例:
// 布尔类型
let bool: boolean = false;
bool = true;
bool = 123; // error 不能将类型"123"分配给类型"boolean"
// 当然了,赋给 bool 的值也可以是一个计算之后结果是布尔值的表达式,比如:
let bool: boolean = !!0
console.log(bool) // false
// 数值类型
let num: number;
num = 123;
num = "123"; // error 不能将类型"123"分配给类型"number"
num = 0b1111011; // 二进制的123
num = 0o173; // 八进制的123
num = 0x7b; // 十六进制的123
// 字符串
let str: string = "toimc";
str = "imooc";
const first = "toimc";
const last = "imooc";
str = `${first} ${last}`;
console.log(str) // 打印结果为:toimc imooc
// 对象类型
class Person {}
const teacher: {
name: string
age: number
} = {
name: 'Zws',
age: 18
}
// 空对象
const obj: object = {}
// number类型的数组
const numbers: number[] = [1, 2, 3]
const arr: Array<any> = [1,2,3,4]
const zws: Person = new Person()
// 对象类型-函数的两种写法儿
// 第一种(可以忽略 number,类型推断会推断出返回值是number)
const func = (str: string): number => {
return parseInt(str, 10)
}
// 第二种(可以理解为:冒号后面跟的是函数的类型,等号后面是函数体。不能忽略number,不然语法错误)
const func1: (str: string) => number = str => {
return parseInt(str, 10)
}
// 如果开启了 strictNullChecks,可选参数会被自动加上|undefined,来看例子:
let str = "toimc";
str = null; // error 不能将类型“null”分配给类型“string”
let strNull: string | null = "lison"; // 这里你可以简单理解为,string | null即表示既可以是string类型也可以是null类型
strNull = null; // right
strNull = undefined; // error 不能将类型“undefined”分配给类型“string | null”
// 如果开启了 strictNullChecks,可选参数会被自动加上|undefined,来看例子:
const sum = (x: number, y?: number) => {
return x + (y || 0);
};
sum(1, 2); // 3
sum(1); // 1
sum(1, undefined); // 1
sum(1, null); // error Argument of type 'null' is not assignable to parameter of type 'number | undefined'
关于null
与undefined
中,扩展知识:
有些情况下编译器是无法在我们声明一些变量前知道一个值是否是 null 的,需要通过!
来告诉编译器该值不为null:
function getNum(num: number | null): string {
// 这里在函数getSplicedStr里定义一个函数getRes,我们最后调用getSplicedStr返回的值实际是getRes运行后的返回值
function getLength(prefix: string) {
// 这里使用参数num,num的类型为number或null
// 在运行前编译器是无法知道在运行时num参数的实际类型的
// 这里如果没有 ! 会报错,因为num参数可能为null
return prefix + num!.toFixed().toString();
}
num = num || 0.1;
return getLength("toimc");
}
# Symbol
(比较特殊,单独拿出来介绍!)
symbol
是 ES6 新增的一种基本数据类型,用来表示独一无二的值。
应用场景:Symbol可以作为属性名,比如:
let name = Symbol();
let obj = {
[name]: "toimc"
};
console.log(obj); // { Symbol(): 'toimc' }
# 需要注意的点
Symbol 前面不能加
new
关键字,直接调用即可创建一个独一无二的 symbol 类型的值const s1 = Symbol('imooc') const s2 = Symbol('imooc') // This condition will always return 'false' since the types 'typeof s1' and 'typeof s2' have no overlap.(2367) s1 === s2 // false
Symbol 类型值作为属性名,这个属性不会被
for…in
遍历到,也不会被Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
获取到;可以使用
Object.getOwnPropertySymbols
方法获取对象的所有symbol类型的属性名;const name1 = Symbol("name"); const obj = { [name1]: "toimc", age: 18 }; const SymbolPropNames = Object.getOwnPropertySymbols(obj); console.log(SymbolPropNames); // [ Symbol(name) ]
可以用 ES6 新提供的 Reflect 对象的静态方法
Reflect.ownKeys
,它可以返回所有类型的属性名:console.log(Reflect.ownKeys(obj)); // ["age", Symbol(name)]
# 静态方法for
和 keyFor
使用 Symbol.for
方法传入字符串,会先检查有没有使用该字符串调用 Symbol.for 方法创建的 symbol 值,如果有,返回该值,如果没有,则使用该字符串新创建一个
const s1 = Symbol("toimc");
const s2 = Symbol("toimc");
const s3 = Symbol.for("toimc");
const s4 = Symbol.for("toimc");
console.log(s3 === s4) // true
console.log(s1 === s3) // false
Symbol.keyFor
该方法传入一个 symbol 值,返回该值在全局注册的键名:
const sym = Symbol.for("toimc");
console.log(Symbol.keyFor(sym)); // 'toimc'
# 内置的 Symbol 值
ES6 提供了 11 个内置的 Symbol 值:
Symbol.hasInstance
:当其他对象使用 instanceof 判断是否为这个对象的实例时,会调用你定义的这个方法const obj = { [Symbol.hasInstance](otherObj) { console.log(otherObj); } }; console.log({ a: "a" } instanceof obj); // false
Symbol.isConcatSpreadable
: 当一个数组的 Symbol.isConcatSpreadable 设为 true 时,这个数组在数组的 concat 方法中不会被扁平化。let arr = [1, 2]; console.log([].concat(arr, [3, 4])); // 打印结果为[1, 2, 3, 4],length为4 let arr1 = ["a", "b"]; console.log(arr1[Symbol.isConcatSpreadable]); // undefined arr1[Symbol.isConcatSpreadable] = false; console.log(arr1[Symbol.isConcatSpreadable]); // false console.log([].concat(arr1, [3, 4])); // [ ["a", "b", Symbol(Symbol.isConcatSpreadable): false], 3, 4 ]
Symbol.species
:后续学习类后再进行介绍,可以看看如下示例:class C extends Array { getName() { return "toimc"; } } const c = new C(1, 2, 3); const a = c.map(item => item + 1); console.log(a); // [2, 3, 4] console.log(a instanceof C); // true console.log(a instanceof Array); // true console.log(a.getName()); // "toimc"
这个例子中,a 是由 c 通过 map 方法衍生出来的,我们也看到了,a 既是 C 的实例,也是 Array 的实例。但是如果我们想只让衍生的数组是 Array 的实例,就需要用 Symbol.species,我们来看下怎么使用:
class C extends Array { // 关键代码: static get [Symbol.species]() { return Array; } getName() { return "toimc"; } } const c = new C(1, 2, 3); const a = c.map(item => item + 1); console.log(a); // [2, 3, 4] // 这里是false console.log(a instanceof C); // false console.log(a instanceof Array); // true console.log(a.getName()); // error a.getName is not a function
就是给类 C 定义一个静态 get 存取器方法,方法名为
Symbol.species
,然后在这个方法中返回要构造衍生数组的构造函数。所以最后我们看到,a instanceof C为 false
,也就是 a 不再是 C 的实例,也无法调用继承自 C 的方法。Symbol.match
:当在字符串 str 上调用 match 方法时,会调用这个方法let obj = { [Symbol.match](string) { return string.length; } }; console.log("abcde".match(obj)); // 5
Symbol.replace
:当在字符串 str 上调用 replace 方法时,会调用这个方法,同上Symbol.search
:当在字符串 str 上调用 search 方法时,会调用这个方法,同上Symbol.split
:当在字符串 str 上调用 split方法时,会调用这个方法,同上Symbol.iterator
:数组的 Symbol.iterator 属性指向该数组的默认遍历器方法:
const arr = [1, 2, 3]; const iterator = arr[Symbol.iterator](); console.log(iterator); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next());
Symbol.toPrimitive
:对象的这个属性指向一个方法,当这个对象被转为原始类型值时会调用这个方法。这个方法有一个参数,即是这个对象被转为的类型,我们来看下:
let obj = { [Symbol.toPrimitive](type) { console.log(type); } }; // const b = obj++ // number const a = `abc${obj}`; // string
Symbol.toStringTag
:Symbol.toStringTag
和 Symbol.toPrimitive
相似,对象的这个属性的值可以是一个字符串,也可以是一个存取器 get 方法,当在对象上调用 toString 方法时调用这个方法,返回值将作为"[object xxx]"中 xxx 这个值:let obj = { [Symbol.toStringTag]: "toimc" }; obj.toString(); // "[object toimc]" let obj2 = { get [Symbol.toStringTag]() { return "yoyo"; } }; obj2.toString(); // "[object yoyo]"
Symbol.unscopables
:这个值和 with 命令有关,但在TS严格模式中,无法使用with。
# 数组和对象
这部分,只是有一些基础的示例,并在后续的篇章展开具体的讨论:
# 数组
两种定义数组的方式:
let list1: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];
第一种,形式通过number[]
的形式来指定这个类型元素均为number类型的数组类型,这种写法是推荐的写法。
注意,这两种写法中的number
指定的是数组元素的类型,你也可以在这里将数组的元素指定为任意类型。
如果你要指定一个数组里的元素既可以是数值也可以是字符串,那么你可以使用这种方式:number|string[]
# 对象
# 对象是引用类型
object
在 JS 中是引用类型。
它和 JS 中的其他基本类型不一样,像 number、string、boolean、undefined、null 这些都是基本类型,而 object 类型的变量存的是引用,看个简单的例子:
let originStr = "abc";
let strClone = originStr;
strClone = "efg";
console.log(originStr); // 'abc' 未改变原值
let objInit = { a: "aa" };
let objClone = objInit;
console.log(objClone) // { a: 'aa'}
objInit.a = "bb";
console.log(objClone); // { a: 'bb' } 改变原值
通过例子可以看出,我们修改 objInit
时,objClone
也被修改了,是因为 objClone
保存的是 objInit
的引用,实际上 objInit
和 objClone
是同一个对象。
当我们希望一个变量或者函数的参数的类型是一个对象的时候,使用这个类型,比如:
let obj: object
obj = { name: 'toimc' }
obj = 123 // error 不能将类型“123”分配给类型“object”
这里有一点要注意了,你可能会想到给 obj 指定类型为 object 对象类型,然后给它赋值一个对象,后面通过属性访问操作符访问这个对象的某个属性,实际操作一下你就会发现会报错:
let obj: object
obj = { name: 'toimc' }
console.log(obj.name) // error 类型“object”上不存在属性“name”
# 应用场景
当一个值必须是对象而不是数值等类型时,比如我们定义一个函数,参数必须是对象,这个时候就用到object类型了:
function getKeys (obj: object) {
return Object.keys(obj) // 会以列表的形式返回obj中的值
}
getKeys({ a: 'a' }) // ['a']
getKeys(123) // error 类型“123”的参数不能赋给类型“object”的参数
这里涉及到的函数的相关知识,我们会在后面章节介绍的,你只要在这里明白object类型的使用就可以了。
# Object vs {} vs object
示例:
var o: object;
o = { prop: 0 }; // OK
o = []; // OK
o = 42; // Error
o = "string"; // Error
o = false; // Error
o = null; // Error
o = undefined; // Error
var p: {}; // or Object
p = { prop: 0 }; // OK
p = []; // OK
p = 42; // OK
p = "string"; // OK
p = false; // OK
p = null; // Error
p = undefined; // Error
var q: { [key: string]: any };
q = { prop: 0 }; // OK
q = []; // OK
q = 42; // Error
q = "string"; // Error
q = false; // Error
q = null; // Error
q = undefined; // Error
var r: { [key: string]: string };
r = { prop: 'string' }; // OK
r = { prop: 0 }; // Error
r = []; // Error
r = 42; // Error
r = "string"; // Error
r = false; // Error
r = null; // Error
r = undefined; // Error
结论:
与
Object
类型相同的{}
是最不具体的,可以将对象、数组和基元分配给它;object
是更具体的,类似于{ [key: string]: any }
;可以给它分配
对象
和数组
,但不能分配原始类型的数据;{ [key: string]: string }
是最具体的,它不允许任何原始类型、数组或具有非字符串值的对象被分配到它。
# 相关示例
// 数组可以是数字和字符串类型
const numberArr: (string | number)[] = [1, 2, 3]
// 即是number类型也可能是string数组
const numbers1: number[] | string[] = ['123', '333'] // 正确
const numbers2: number[] | string[] = [123, '333'] // 错误,不能两种类型混用
const numbers3: Array<number|string> = [123, '333'] // 正确
const arr: (string|number)[] = [123,'string'] // 正确
// undefined 数组
const undefinedArr: undefined[] = [undefined, undefined, undefined]
// 存储对象类型的内容
const objectArr: { name: string; age: number }[] = [
{
name: 'zws',
age: 18
}
]
// 使用类型别名(type alias)
type User = { name: string; age: number }
// 存储对象类型的内容
const objectArr: User[] = [
{
name: 'zws',
age: 18
}
]
// 关于 Class
class Teacher {
name: string
age: number
}
const objectArr: Teacher[] = [
new Teacher(),
{
name: 'zws',
age: 18
}
]
这里的
Array<string|number>
是联合类型,见后续。