你还不会Typescript吗?(二)基础类型

废话不多说 直接看用法

环境配置

本地环境

.ts 文件是不可以直接执行的,需要编译为.js文件,才能够进行运行。那现在就开始配置 TypeScript 的环境吧。

tsc 的安装与使用

tsc 是 typescript compiler 的缩写,即 ts 的编译器。

# npm 全局安装 TypeScript
$ npm install typescript -g
# 检查是否存在 tsc 环境变量,配置正确时会回应tsc的版本号
$ tsc -v

# 尝试创建ts文件并写入一些内容
$ touch demo.ts && echo 'console.log("Hello ts!")' > demo.ts

# 将ts文件编译为同名js文件后运行
$ tsc demo.ts && node demo.js

# 将当前目录下所有文件编译为同名js文件
$ tsc * 

上方的例子分为了两步(先转换在运行),可以通过 npm(ts-node),来进行合并操作:

$ npm install ts-node -g
# 直接进行运行
$ ts-node demo.ts

基础类型

基本类型

基础类型: 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: 'tikc',
  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```typescript
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.toStringTagSymbol.toStringTagSymbol.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
  }
]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嘴巴嘟嘟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值