TS学习笔记

安装TS

  • 命令行运行如下命令,全局安装 TypeScript:
	npm install -g typescript
  • 安装完成后,在控制台运行如下命令,检查安装是否成功(3.x):
	tsc -V 

第一个TS代码

// hello.ts
function greeter (person:string) {
  return 'Hello, ' + person
}

let user = 'Yee'

console.log(greeter(user))
//html
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<script type="text/javascript" src='./hello.ts'></script>
<body>
	
</body>
</html>
  • 页面引入后报错
  • 在这里插入图片描述
  • 浏览器不能直接识别TS代码,要把TS代码转化为JS代码才可以执行
  • TS代码转化为JS,在命令行上,通过 Node.js 运行这段代码
tsc hello.ts
  • 转化后的TS文件代码
//hello.js
function greeter(person) {
    return 'Hello, ' + person;
}
var user = 'Yee';
console.log(greeter(user));
  • 修改引入的文件
//html
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<script type="text/javascript" src='./hello.js'></script>
<body>
	
</body>
</html>
  • 控制台输出:Hello, Yee

vscode自动转化TS文件

  1. tsc --init 生成配置文件tsconfig.json
  2. 修改tsconfig.json配置: “outDir”: “./js”,“strict”: false,
  3. 启动监视任务: 终端 -> 运行任务 -> 监视tsconfig.json
  • 第三步可能会遇到BUG:
  • 解决办法:http://www.jsphp.net/soft/show-31-328-1.html

类型的注解

  • 给 person 函数的参数添加 : string 类型注解
function greeter (person: string) {
  return 'Hello, ' + person
}

let user = 'Yee'

console.log(greeter(user))
  • TypeScript 里的类型注解是一种轻量级的为函数或变量添加约束的方式。 在这个例子里,我们希望 greeter 函数接收一个字符串参数。 然后尝试把 greeter 的调用改成传入一个数组
function greeter (person: string) {
  return 'Hello, ' + person
}

let user = [0, 1, 2]

console.log(greeter(user))
  • 重新编译,你会看到产生了一个错误:
  • error TS2345: Argument of type ‘number[]’ is not assignable to parameter of type ‘string’.
  • 类似地,尝试删除 greeter 调用的所有参数。 TypeScript 会告诉你使用了非期望个数的参数调用了这个函数。 在这两种情况中,TypeScript提供了静态的代码分析,它可以分析代码结构和提供的类型注解。
  • 要注意的是尽管有错误,greeter.js 文件还是被创建了。 就算你的代码里有错误,你仍然可以使用 TypeScript。但在这种情况下,TypeScript 会警告你代码可能不会按预期执行。

基础类型

布尔值

  • 最基本的数据类型就是简单的 true/false 值,在JavaScript 和 TypeScript 里叫做 boolean(其它语言中也一样)。
let isDone: boolean = false;
isDone = true;
// isDone = 2 // error

数字

  • 和 JavaScript 一样,TypeScript 里的所有数字都是浮点数。 这些浮点数的类型是 number。 除了支持十进制和十六进制字面量,TypeScript 还支持 ECMAScript 2015中引入的二进制和八进制字面量。
let a1: number = 10 // 十进制
let a2: number = 1010  // 二进制
let a3: number = 712 // 八进制
let a4: number = 151 // 十六进制

字符串

  • JavaScript 程序的另一项基本操作是处理网页或服务器端的文本数据。 像其它语言里一样,我们使用 string 表示文本数据类型。 和 JavaScript 一样,可以使用双引号(")或单引号(’)表示字符串。
let name:string = 'tom'
name = 'jack'
// name = 12 // error
let age:number = 12
const info = `My name is ${name}, I am ${age} years old!`

undefined 和 null

  • TypeScript 里,undefined 和 null 两者各自有自己的类型分别叫做 undefined 和 null。 它们的本身的类型用处不是很大:
let u: undefined = undefined
let n: null = null
  • 默认情况下 null 和 undefined 是所有类型的子类型。 就是说你可以把 null 和 undefined 赋值给 number 类型的变量。
let u: undefined = undefined
let n: null = null
let m:number = null
let z:string = undefined

数组

  • TypeScript 像 JavaScript 一样可以操作数组元素。 有两种方式可以定义数组。 第一种,可以在元素类型后面接上[],表示由此类型元素组成的一个数组:
let list1: number[] = [1, 2, 3]
  • 第二种方式是使用数组泛型,Array<元素类型>:
let list2: Array<number> = [1, 2, 3]

元组 Tuple

  • 元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为 string 和 number 类型的元组。
let t1: [string, number]
t1 = ['hello', 10] // OK
t1 = [10, 'hello'] // Error

枚举

  • enum 类型是对 JavaScript 标准数据类型的一个补充。 使用枚举类型可以为一组数值赋予友好的名字
enum Color {
  Red,
  Green,
  Blue
}

// 枚举数值默认从0开始依次递增
// 根据特定的名称得到对应的枚举数值
let myColor: Color = Color.Green  // 0
console.log(myColor, Color.Red, Color.Blue)
  • 默认情况下,从 0 开始为元素编号。 你也可以手动的指定成员的数值。 例如,我们将上面的例子改成从 1 开始编号:
enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green
  • 或者,全部都采用手动赋值:
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green
  • 枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 例如,我们知道数值为 2,但是不确定它映射到 Color 里的哪个名字,我们可以查找相应的名字:
enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2]

console.log(colorName)  // 'Green'

any

  • 有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用 any 类型来标记这些变量
let notSure: any = 4
notSure = 'maybe a string'
notSure = false // 也可以是个 boolean
  • 在对现有代码进行改写的时候,any 类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。并且当你只知道一部分数据的类型时,any 类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据:
let list: any[] = [1, true, 'free']

list[1] = 100

void

  • 某种程度上来说,void 类型像是与 any 类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void:
/* 表示没有任何类型, 一般用来说明函数的返回值不能是undefined和null之外的值 */
function fn(): void {
  console.log('fn()')
  // return undefined
  // return null
  // return 1 // error
}
  • 声明一个 void 类型的变量没有什么大用,因为你只能为它赋予 undefined 和 null:
let unusable: void = undefined

object

  • object 表示非原始类型,也就是除 number,string,boolean之外的类型。
  • 使用 object 类型,就可以更好的表示像 Object.create 这样的 API。例如:
function fn2(obj:object):object {
  console.log('fn2()', obj)
  return {}
  // return undefined
  // return null
}
console.log(fn2(new String('abc')))
// console.log(fn2('abc') // error
console.log(fn2(String))

联合类型

  • 联合类型(Union Types)表示取值可以为多种类型中的一种
  • 需求1: 定义一个一个函数得到一个数字或字符串值的字符串形式值
function toString2(x: number | string) : string {
  return x.toString()
}
  • 需求2: 定义一个一个函数得到一个数字或字符串值的长度
function getLength(x: number | string) {

  // return x.length // error

  if (x.length) { // error
    return x.length
  } else {
    return x.toString().length
  }
}

类型断言

  • 通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript 会假设你,程序员,已经进行了必须的检查。
  • 类型断言有两种形式。 其一是“尖括号”语法, 另一个为 as 语法
/* 
类型断言(Type Assertion): 可以用来手动指定一个值的类型
语法:
    方式一: <类型>值
    方式二: 值 as 类型  tsx中只能用这种方式
*/

/* 需求: 定义一个函数得到一个字符串或者数值数据的长度 */
function getLength(x: number | string) {
  if ((<string>x).length) {
    return (x as string).length
  } else {
    return x.toString().length
  }
}
console.log(getLength('abcd'), getLength(1234))

类型推断

  • 类型推断: TS会在没有明确的指定类型的时候推测出一个类型
  • 有下面2种情况: 1. 定义变量时赋值了, 推断为对应的类型. 2. 定义变量时没有赋值, 推断为any类型
/* 定义变量时赋值了, 推断为对应的类型 */
let b9 = 123 // number
// b9 = 'abc' // error

/* 定义变量时没有赋值, 推断为any类型 */
let b10  // any类型
b10 = 123
b10 = 'abc'

接口

初识接口

  • 这里我们使用接口来描述一个拥有 firstName 和 lastName 字段的对象。
interface Person {
  firstName: string
  lastName: string
}

function greeter (person: Person) {
  return 'Hello, ' + person.firstName + ' ' + person.lastName
}
// 参数必须符合interface的定义
let user = {
  firstName: 'Yee',
  lastName: 'Huang'
}

console.log(greeter(user))
  • 让我们创建一个 User 类,它带有一个构造函数和一些公共字段。因为类的字段包含了接口所需要的字段,所以他们能很好的兼容。
  • 还要注意的是,我在类的声明上会注明所有的成员变量,这样比较一目了然。
class User {
  fullName: string
  firstName: string
  lastName: string

  constructor (firstName: string, lastName: string) {
    this.firstName = firstName
    this.lastName = lastName
    this.fullName = firstName + ' ' + lastName
  }
}

interface Person {
  firstName: string
  lastName: string
}

function greeter (person: Person) {
  return 'Hello, ' + person.firstName + ' ' + person.lastName
}

let user = new User('Yee', 'Huang')

console.log(greeter(user))

可选属性

  • 接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。
  • 带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个 ? 符号。
interface IPerson {
  id: number
  name: string
  age: number
  sex?: string
}

只读属性

  • 一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly 来指定只读属性:
interface IPerson {
  readonly id: number
  name: string
  age: number
  sex?: string
}
  • 一旦赋值后再也不能被改变了。
const person2: IPerson = {
  id: 2,
  name: 'tom',
  age: 20,
  // sex: '男' // 可以没有
  // xxx: 12 // error 没有在接口中定义, 不能有
}
person2.id = 2 // error

readonly vs const

  • 最简单判断该用 readonly 还是 const 的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用 readonly

函数类型

  • 参数列表里的每个参数都需要名字和类型。
/* 
接口可以描述函数类型(参数的类型与返回的类型)
*/

interface SearchFunc {
  (source: string, subString: string): boolean
}
  • 这样定义后,我们可以像使用其它接口一样使用这个函数类型的接口。 下例展示了如何创建一个函数类型的变量,并将一个同类型的函数赋值给这个变量。
const mySearch: SearchFunc = function (source: string, sub: string): boolean {
  return source.search(sub) > -1
}

console.log(mySearch('abcd', 'bc'))

类类型

类实现接口

  • 与 C# 或 Java 里接口的基本作用一样,TypeScript 也能够用它来明确的强制一个类去符合某种契约
/* 
类类型: 实现接口
1. 一个类可以实现多个接口
2. 一个接口可以继承多个接口
*/

interface Alarm {
  alert(): any;
}


class Car implements Alarm {
  alert() {
      console.log('Car alert');
  }
}

一个类可以实现多个接口

interface Alarm {
  alert(): any;
}

interface Light {
  lightOn(): void;
  lightOff(): void;
}
class Car2 implements Alarm, Light {
  alert() {
    console.log('Car alert');
  }
  lightOn() {
    console.log('Car light on');
  }
  lightOff() {
    console.log('Car light off');
  }
}


接口继承接口

  • 和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。
interface Alarm {
  alert(): any;
}
interface Light {
  lightOn(): void;
  lightOff(): void;
}
interface LightableAlarm extends Alarm,Light {

}

  • 对于传统的 JavaScript 程序我们会使用函数和基于原型的继承来创建可重用的组件,但对于熟悉使用面向对象方式的程序员使用这些语法就有些棘手,因为他们用的是基于类的继承并且对象是由类构建出来的。 从 ECMAScript 2015,也就是 ES6 开始, JavaScript 程序员将能够使用基于类的面向对象的方式。 使用 TypeScript,我们允许开发者现在就使用这些特性,并且编译后的 JavaScript 可以在所有主流浏览器和平台上运行,而不需要等到下个 JavaScript 版本。

基本示例

/* 
类的基本定义与使用
*/

class Greeter {
  // 声明属性
  message: string

  // 构造方法
  constructor (message: string) {
    this.message = message
  }

  // 一般方法
  greet (): string {
    return 'Hello ' + this.message
  }
}

// 创建类的实例
const greeter = new Greeter('world')
// 调用实例的方法
console.log(greeter.greet())

继承

/* 
类的继承
*/

class Animal {
  constructor(name,age){
	this.name=name
	this.age=age
  }
  run (distance: number) {
    console.log(`Animal run ${distance}m`)
  }
}

class Dog extends Animal {
  constructor(name,age,sex){
    super(name,age) //super继承原型属性的关键字,super必须放在最上面
	this.sex = sex 
  }
  cry () {
    console.log('wang! wang!')
  }
  run(){
    //重写方法:因为原型链是按照隐式原型查找的,找到Dog构造函数实例上的run方法后就会停止查找,不会再去找Animal构造函数实例上的run方法
  }
}

const dog = new Dog()
dog.cry() 
dog.run(100) // 可以调用从父中继承得到的方法

多态

  • 多态:父类定义一个方法不去实现,让继承他的子类去实现,每一个子类有不同的表现,多态也属于继承
class Futher {
    public age: number;
    constructor(age: number) {
        this.age = age
    }
    counts(): void {
        console.log(this.age)
    }
}
class children1 extends Futher {
    constructor(age: number) {
        super(age)
    }
    counts(): void {    /* 多态,重写方法不执行父类方法 */
        console.log(this.age - 1)
    }
}
class children2 extends Futher {
    constructor(age: number) {
        super(age)
    }
    counts(): void {
        console.log(this.age + 1)
    }
}

公共,私有与受保护的修饰符

默认为 public

  • 在 TypeScript 里,成员都默认为 public,你也可以明确的将一个成员标记成 public

理解 private

  • 当成员被标记成 private 时,它就不能在声明它的类的外部访问

理解 protected

  • protected 修饰符与 private 修饰符的行为很相似,但有一点不同,protected成员在派生类中仍然可以访问
//父类
class Animal {
  public name: string
  //构造函数
  public constructor (name: string) {
    this.name = name
  }

  public run (distance: number=0) {
    console.log(`${this.name} run ${distance}m`)
  }
}
//派生类
class Person extends Animal {
  //隐私属性
  private age: number = 18
  //受保护的属性,在派生类中使用
  protected sex: string = '男'
  //构造函数
  constructor (name:string,age:number,sex:string) {
    super(name)
    this.age=age
    this.sex=sex
  }
  run (distance: number=5) {
    console.log('Person jumping...')
    super.run(distance)
  }
}
class Student extends Person {
  constructor(name:string,age:number,sex:string){
    super(name,age,sex)
  }
  run (distance: number=6) {
    console.log('Student jumping...')

    console.log(this.sex) // 子类能看到父类中受保护的成员
    // console.log(this.age) //  子类看不到父类中私有的成员

    super.run(distance)
  }
}
console.log(new Person('s',18,'南'))
console.log(new Person('s',18,'南').name) // 公开的可见
// console.log(new Person('s',18,'南').sex) // 受保护的不可见
console.log(new Student('s',18,'南').run())
// console.log(new Person('s',18,'南').age) //  私有的不可见

readonly 修饰符

  • 你可以使用 readonly 关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
class Person {
  readonly name: string = 'abc'
  constructor(name: string) {
    this.name = name
  }
}

let john = new Person('John')
// john.name = 'peter' // error

参数属性

  • 在上面的例子中,我们必须在 Person 类里定义一个只读成员 name 和一个参数为 name 的构造函数,并且立刻将 name 的值赋给 this.name。
  • 参数属性可以方便地让我们在一个地方定义并初始化一个成员。 下面的例子是对之前 Person 类的修改版,使用了参数属性:
class Person2 {
  constructor(readonly name: string) {
  }
}

const p = new Person2('jack1')
console.log(p.name)
// p.name='111' //err 只读属性,不能被重新赋值
  • 注意看我们是如何舍弃参数 name,仅在构造函数里使用 readonly name: string 参数来创建和初始化 name 成员。 我们把声明和赋值合并至一处。
  • 参数属性通过给构造函数参数前面添加一个访问限定符来声明。
  • 使用 private 限定一个参数属性会声明并初始化一个私有成员;对于 public 和 protected 来说也是一样

存取器

  • TypeScript 支持通过 getters/setters 来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问
class Person {
  firstName: string = 'A'
  lastName: string = 'B'
  get fullName () {
    return this.firstName + '-' + this.lastName
  }
  set fullName (value) {
    const names = value.split('-')
    this.firstName = names[0]
    this.lastName = names[1]
  }
}

const p = new Person()
console.log(p.fullName)

p.firstName = 'C'
p.lastName =  'D'
console.log(p.fullName)

p.fullName = 'E-F'
console.log(p.firstName, p.lastName)

静态属性

  • 到目前为止,我们只讨论了类的实例成员,那些仅当类被实例化的时候才会被初始化的属性。 我们也可以创建类的静态成员,这些属性存在于类本身上面而不是类的实例上
/* 
静态属性, 是类对象的属性
非静态属性, 是类的实例对象的属性
*/
class Person {
  name1: string = 'A'
  static name2: string = 'B'
}

console.log(Person.name2)
console.log(new Person().name1) 
// console.log(new Person().name2) //err 静态属性不能被实例对象访问

抽象类

  • 抽象类做为其它派生类的基类使用。 它们不能被实例化
/* 
抽象类
  不能创建实例对象, 只有实现类才能创建实例
  可以包含未实现的抽象方法
*/

abstract class Animal {

  abstract cry ()

  run () {
    console.log('run()')
  }
}

class Dog extends Animal {
  cry () {
    console.log(' Dog cry()')
  }
}

const dog = new Dog()
dog.cry()
dog.run()

函数

  • 在 TypeScript 里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义行为的地方。

基本示例

  • 和 JavaScript 一样,TypeScript 函数可以创建有名字的函数和匿名函数。你可以随意选择适合应用程序的方式,不论是定义一系列 API 函数还是只使用一次的函数。
  • 通过下面的例子可以迅速回想起这两种 JavaScript 中的函数:
// 命名函数
function add(x, y) {
  return x + y
}

// 匿名函数
let myAdd = function(x, y) { 
  return x + y;
}

函数类型

为函数定义类型

  • 让我们为上面那个函数添加类型:
//参数X,Y为number类型,并且返回值也是number类型
function add(x: number, y: number): number {
  return x + y
}
//参数X,Y为number类型,并且返回值也是number类型
let myAdd = function(x: number, y: number): number { 
  return x + y
}

可选参数和默认参数

  • TypeScript 里的每个函数参数都是必须的,而JavaScript 里,每个参数都是可选的,可传可不传。
  • JS没传参的时候,它的值就是 undefined。 在TypeScript 里我们可以在参数名旁使用 ? 实现可选参数的功能。
  • 在 TypeScript 里,我们也可以为参数提供一个默认值
// 我们想让 lastName 是可选的
function buildName(firstName: string='A', lastName?: string): string {
  if (lastName) {
    return firstName + '-' + lastName
  } else {
    return firstName
  }
}

console.log(buildName('C', 'D'))
console.log(buildName('C'))
console.log(buildName())

剩余参数

  • 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。
  • 在 JavaScript 里,你可以使用 arguments 来访问所有传入的参数。
function Person(b){
  	console.log(b)
  	console.log(arguments)
  }
  Person(1,2,3,4,3)
  • 在 TypeScript 里,你可以把所有参数收集到一个变量里,剩余参数要放在末尾的位置
function info(x: string, ...args: string[]) {
  console.log(x, args)
}
info('abc', 'c', 'b', 'a')

函数重载

  • 函数重载: 函数名相同, 而形参不同的多个函数
  • 在JS中, 由于弱类型的特点和形参与实参可以不匹配, 是没有函数重载这一说的 但在TS中, 与其它面向对象的语言(如Java)就存在此语法
/* 
函数重载: 函数名相同, 而形参不同的多个函数
需求: 我们有一个add函数,它可以接收2个string类型的参数进行拼接,也可以接收2个number类型的参数进行相加 
*/

// 重载函数声明
function add (x: string, y: string): string
function add (x: number, y: number): number

// 定义函数实现
function add(x: string | number, y: string | number): string | number {
  // 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 x + y
  if (typeof x === 'string' && typeof y === 'string') {
    return x + y
  } else if (typeof x === 'number' && typeof y === 'number') {
    return x + y
  }
}

console.log(add(1, 2))
console.log(add('a', 'b'))
// console.log(add(1, 'a')) // error

泛型

  • 指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性

正常使用

function createArray(value: any, count: number): any[] {
  const arr: any[] = []
  for (let index = 0; index < count; index++) {
    arr.push(value)
  }
  return arr
}

const arr1 = createArray(11, 3)
const arr2 = createArray('aa', 3)
console.log(arr1[0].toFixed(), arr2[0].split(''))

使用函数泛型

function createArray2 <T> (value: T, count: number):Array<T> {
  const arr: Array<T> = []
  for (let index = 0; index < count; index++) {
    arr.push(value)
  }
  return arr
}
const arr3 = createArray2<number>(11, 3)
console.log(arr3[0].toFixed())
// const arr33 = createArray2<number>('11', 3) //number类型不能赋值字符串
// console.log(arr3[0].split('')) // error
const arr4 = createArray2<string>('aa', 3)
console.log(arr4[0].split(''))
// console.log(arr4[0].toFixed()) // error

多个泛型参数的函数

function swap <K, V> (a: K, b: V): [K, V] {
  return [a, b]
}
const result = swap<string, number>('abc', 123)
console.log(result[0].length, result[1].toFixed())

泛型接口

interface IbaseCRUD <T> {
  //data是一个数组,且数组的元素中必须含有 name 和age 属性
  data: T[]
  //add是一个方法,且方法中的参数必须有name和age属性,可以没有返回值
  add: (t: T) => void
  //getById是一个方法,参数id必须为number类型,返回值中必须包含name和age属性
  getById: (id: number) => T
}

class User {
  //属性类型描述
  id?: number; //id主键自增
  name: string; //姓名
  age: number; //年龄

  constructor (id:number,name:string, age:number) {
    this.id=id
    this.name = name
    this.age = age
  }
}

class UserCRUD implements IbaseCRUD <User> {
  data: User[] = []
  

  //参数user对象必须包括name和age对象,id属性不确定
  add(user: User): void {
    console.log(user)
    this.data.push(user)
    console.log('保存user', user.id)
  }

  getById(id: number): User {
    return this.data.find(item => item.id===id)
  }
}


const userCRUD = new UserCRUD()
console.log(userCRUD)
// console.log(userCRUD.name) //err 不存在name属性
userCRUD.add(new User(1,'tom', 12))
userCRUD.add(new User(2,'tom2', 13))
console.log(userCRUD.getById(1))

泛型类

class GenericNumber<T> {
  zeroValue: T
  add: (x: T, y: T) => T
}

let myGenericNumber = new GenericNumber<number>()
myGenericNumber.zeroValue = 0
myGenericNumber.add = function(x, y) {
  return x + y 
}

let myGenericString = new GenericNumber<string>()
myGenericString.zeroValue = 'abc'
myGenericString.add = function(x, y) { 
  return x + y
}
//string型
console.log(myGenericString.add(myGenericString.zeroValue, 'test'))
//number类型
console.log(myGenericNumber.add(myGenericNumber.zeroValue, 12))

泛型约束

  • 如果我们直接对一个泛型参数取 length 属性, 会报错, 因为编译器并不能证明每种类型都有length属性,所以就报错了
// 没有泛型约束
function fn <T>(x: T): void {
  // console.log(x.length)  // error
}
  • 相比于操作any所有类型,我们想要限制函数去处理任意带有.length属性的所有类型。 只要传入的类型有这个属性,我们就允许,就是说至少包含这一属性
interface Lengthwise {
  length: number;
}

// 指定泛型约束,传入的属性必须有length属性
function fn2 <T extends Lengthwise>(x: T): void {
  console.log(x.length)
}
fn2('abc')
// fn2(123) // error  number没有length属性
  • 现在这个泛型函数被定义了约束,因此它不再是适用于任意类型

其他

内置对象

  • JavaScript 中有很多内置对象,它们可以直接在 TypeScript 中当做定义好了的类型
  • 内置对象是指根据标准在全局作用域(Global)上存在的对象。这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准。

ECMAScript 的内置对象

// Boolean Number 等都是对象,不是基础数据类型
let b: Boolean = new Boolean(1)
let n: Number = new Number(156465)
let s: String = new String('abc')
let d: Date = new Date()
let r: RegExp = /^1/
let e: Error = new Error('error message')
//报错, bb是Boolean对象类型
// let bb: boolean = new Boolean(2)  // error
console.log(bb)
console.log(n)

BOM 和 DOM 的内置对象

const div: HTMLElement = document.getElementById('test')
const divs: NodeList = document.querySelectorAll('div')
document.addEventListener('click', (event: MouseEvent) => {
  console.dir(event.target)
})
const fragment: DocumentFragment = document.createDocumentFragment()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值