# 基础类型

# 基本类型

基础类型: booleannumberstring,  symbolnull 和undefined

对象类型: {} 或者 object,  [] 或者 Array<any>, 还有function, Class类型

注意:

  • numberNumber的区别:TS中指定类型的时候要用number,这个是TypeScript的类型关键字。而Number为JavaScript的原生构造函数,用它来创建数值类型的值;
  • TypeScript 和 JavaScript 一样,所有数字都是浮点数,所以只有一个number类型,而没有int或者float类型;
  • TypeScript 还支持 ES6 中新增的二进制和八进制数字字面量,TypeScript 中共支持二、八、十和十六四种进制的数值;
  • 默认情况下 undefinednull 可以赋值给任意类型的值;当你在 tsconfig.json"compilerOptions"里设置了"strictNullChecks": true时,那必须严格对待,undefinednull 将只能赋值给它们自身和 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'

关于nullundefined中,扩展知识:

有些情况下编译器是无法在我们声明一些变量前知道一个值是否是 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)] 
    

# 静态方法forkeyFor

使用 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
    

    image-20210720161141400

  • 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());
    

    image-20210720161622141

  • Symbol.toPrimitive:对象的这个属性指向一个方法,当这个对象被转为原始类型值时会调用这个方法。

    这个方法有一个参数,即是这个对象被转为的类型,我们来看下:

    let obj = {
      [Symbol.toPrimitive](type) {
        console.log(type);
      }
    };
    // const b = obj++ // number
    const a = `abc${obj}`; // string
    
  • Symbol.toStringTagSymbol.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 的引用,实际上 objInitobjClone 是同一个对象。

当我们希望一个变量或者函数的参数的类型是一个对象的时候,使用这个类型,比如:

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>联合类型,见后续。