TypeScript基础

TypeScript基础笔记(主要注意点)

  • 类型
  • 语法扩展
  • 工具

强类型、弱类型:区别在于变量能否被赋值为其它类型
动态类型、静态类型:区别在于动态类型变量的类型是在编译阶段确定的,而静态类型变量类型是在运行时确定的

1. 基本数据类型

void 0返回一个undefined,undefined在JavaScript中不是保留字,可以当做变量使用
string、number、boolean、undefined、null、symbol、any、void、unknown、never
unknown可以被赋值为任意类型,unknown只能被赋值给any和unknown类型,unknown不能进行属性读取操作

let bool: boolean = false
let boolObj: Boolean = new Boolean(1) // 基本包装类型是函数,typeof object
let boolVal: boolean = Boolean(1) // 转型函数 typeof boolean
console.log(typeof bool)
console.log(typeof boolObj)
console.log(typeof boolVal)

let decLiteral: number = 10 // 十进制
let hexLiteral: number = 0xf00d // 十六进制以0x开头
let binaryLiteral: number = 0b0101 // ES6中二进制以0b开头
let octalLiteral: number = 0o070 // ES6中八进制以0o开头
let notANumber: number = NaN
let infinity: Number = Infinity

let str =  `hell world${decLiteral}`

let sym: symbol = Symbol('hello')

let u: undefined = undefined
let n: null = null

// 设置strictNullChecks: false,可以把undefined和null赋值给其它类型,因为以上两种类型都是其它类型的子类型
str = null
decLiteral = undefined

// void类型表示没有返回值的函数,(在JavaScript中,undefined不是保留字,可以作为变量进行赋值,以void来代替其作为没有返回值的函数的返回值,void只能被赋值为undefined和null
let notReturn = (): void => {}

// any类型,任意类型
// 任意类型可以被赋值为其它类型,对任意类型的任何操作都将返回任意类型,如果一个变量没有声明为特定类型,那么他会被推断为任意类型,兜底类型,可以访问它的任意属性和方法

// never类型表示永远也不存在的类型,比如抛出异常的函数和无限循环函数,以及箭头函数表达式类型
const error = () => { throw new Error('err')}
const endless = () => {
  while (true) {}
}
// 以上两个函数的返回值都是never类型
type Foo = string | boolean | number
const controlFlowAnalysisWithNever = (foo: Foo) => {
  if (typeof foo === 'string') {

  } else if (typeof foo === 'boolean') {

  } else if (typeof foo === 'number') {

  } else {
    const bar: never = foo
  }
}
// 防止联合类型中有一个类型没有被实现

// 类型推论
// 如果一个变量没有指定明确的类型,TypeScript会根据类型推论推断出一个类型
// 如果一个变量声明时没有指定类型,也没有赋值,那么他会被推论为any类型

2. 引用类型

1. tuple(元组)

tuple 可以越界添加,但不能越界访问

const tuple: [string, number] = ['hamling', 28]
tuple.push('hello') // 虽然能push成功
// tuple[2] 不能越界读取
2. enum (枚举)
  // 1.数字枚举
  // 值从0依次递增,可以设置初始值,如果设置了初始值,后续值会基于它递增
  // 数字枚举可以通过键值互相访问
  enum Directions {
    North,
    South,
    East = 4,
    West
  }

  // 2.字符串枚举
  // 每一个值必须是字符串字面量或者另一个枚举的成员
  // 字符串枚举在键值相同的情况下互相访问,否则不能互相访问
  enum Status {
    PENDING = 'PENDING',
    FULFILLED = 'FULFILLED',
    REJECTED = 'hello'
  }

  console.log(Status.REJECTED)
  // console.log(Status.hello) // 不能访问

  // 3.常量枚举
  // 通过const关键字修饰的枚举,常量枚举会使用内联语法,不会为枚举类型编译生成任何JavaScript
  const enum Color {
    Red,
    Green,
    Blue
  }

  console.log(Color.Red === 0)

  // 4.异构枚举
  // 枚举成员是数字和字符串的混合
  // 异构枚举可以反向映射,键值键可以相互访问
  enum E {
    A,
    B,
    C = 'C',
    D = 4
  }

  // 常数项和计算所得项
  // 编译时计算(静态)
  // 运行时计算(动态)后面的成员必须进行初始化
  enum F {
    A,
    B = 'blue'.length,
    C = 3 + 1
  }

  console.log(F.B)
4. 函数和数组

数组类型定义的三种方式:类型+[]、数组泛型(Array)和接口定义。

// 数组类型
const arr1: number[] = [1, 2, 3]

const arr2: Array<string> = ['hello', 'world']

interface NamesArray {
  [index: number]: string | number;
  [key: string]: any; // 数字索引必须兼容字符串索引
}
// 函数类型
// 函数定义的几种形式
export function add (x: number, y: number): number {
  return x + y
}

let add1: (x: number, y: number) => number = add

type Add = (x: number, y: number) => number
let add2: Add = add

interface Add1 {
  (x: number, y: number): number;
}
let add3: Add1 = add2

interface SearchFunc {
  (source: string, substring: string): boolean;
}

let search: SearchFunc = (source, substring) => source.search(substring) !== -1

// 函数重载
function getTotal (...rest: string[]): string
function getTotal (...rest: number[]): number
function getTotal (...rest: string[] | number[]): string | number {
  const first = rest[0]
  if (typeof first === 'string') return rest.join('')
  return (rest as number[]).reduce((prev, cur) => prev + cur)
}

function reverse (x: string): string
function reverse (x: number): number
function reverse (x: string | number): string | number {
  if (typeof x === 'string') return x.split('').reverse().join('')
  return Number(x.toString().split('').join(''))
}
5. 接口

interface 接口描绘对象的形状;抽象类的行为,具体行为通过类去实现

interface IPerson {
  name: string // 必选属性
  age?: number // 可选属性
  readonly id: number // 只读属性
  [prop: string]: string | number | undefined // 任意属性,任意属性的类型为其他类型的联合类型
}
// 混合类型
interface Lib {
  (): void;
  version: string;
  doSomething(): void;
}

function getLib(version: string) {
  const lib: Lib = (() => { }) as Lib
  lib.version = version
  lib.doSomething = () => { }
  return lib
}
6. 类

ES5通过构造函数实现类,通过原型实现继承
类(Class)定义了一件事物的抽象特点,包含它的属性和方法
对象(Object)类的实例,通过new生成
面向对象(OOP)的三大特性:封装、继承、多态
封装(Encapusulation)将对数据的操作细节隐藏起来,只暴露对外的接口。外部调用端不需要(也不可能知道)细节,就能通过对外的接口来访问对象,同时也保证了外界无法任意更改对象内部的特点
继承(inheritance)子类继承父类,子类除了拥有父类的所有特性外,还能又有更具体的特性
多态(Polymorphism)由继承而产生相关的不同类,对同一个方法可以有不同的响应
存储器(getter和setter)用于改变属性的读取和响应行为。
修饰符(Modifier)修饰符是一些关键字,用于限定成员和类型的性质。
抽象类(Abstract Class)抽象类是提供其他其他类继承的基类,抽象类不允许被实例化。抽象类的抽象方法必须在子类中被实现。
接口(interface)不同类之间的共有的属性或方法,可以抽象成一个接口,接口可以被类实现(implements)。一个类只能继承自另一个类,但可以实现多个接口

TypeScript可以使用三种修饰符分别是public、private和protected。
public修饰的属性和方法是公有的,可以在任何地方被访问到,默认所有的方法和属性都是公有的;
private修饰的属性和方法是私有的,不能在声明它的类的外部被访问;
protected修饰的属性和方法是受保护的,它和private类似,区别是它在子类中也是允许被访问到。

abstract用于定义抽象类和其中的抽象方法,抽象类不允许被实例化,抽象类中的抽象方法必须被子类实现

类实现接口。实现(implements)是面向对象中一个重要的概念。一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些共同点特性,这时就可以把特性提取成接口,用implements关键字来实现。这个特性大大提高了面向对象的灵活性。
接口继承接口、接口继承类。接口继承类和接口继承接口没有本质的区别,但构造函数、静态属性和静态方法是无法继承的(私有属性存疑)。


class Dog {
  name: string // 实例属性必须声明才能赋值,也可以在构造函数参数中通过public声明
  readonly id: number = 10010
  constructor (name: string, public age: number) {
    this.name = name
    this.age = age
  }
  private eat () {

  }
}

let dog = new Dog('wangwang', 10)

// 类成员修饰符
// 默认情况下类实例属性和原型方法默认都是通过public修饰符修饰的
// private 私有成员,只能在类中使用,不能在实例和子类中使用
// protect 受保护成员,只能在类中和子类中使用,不能在实例中使用
// readonly 只读属性
// static 静态属性 可以被子类继承
// private 修饰constructor,该类既不能被实例化也不能被继承
// protect 修饰constructor,该类不能被实例化但可以被继承(基类)

// 抽象类只能被继承,不能被实例化,抽象方法必须被子类实现
abstract class Person {
  constructor (public name: string) {
    this.name = name
  }
  eat () {}
  abstract sayHi(): void
}

class Student extends Person {
  constructor (name: string) {
    super(name)
  }
  sayHi () {}
}

class Teacher extends Person {
  sayHi () {}
}

const persons = [new Student('std'), new Teacher('tea')]
persons.forEach(item => item.sayHi())

class Flow {
  step1 () {
    return this
  }
  step2 () {
    return this
  }
}

let flow = new Flow()
flow.step1().step2()

class WorkFlow extends Flow {
  next () {
    return this
  }
}

let workFlow = new WorkFlow()
workFlow.step1().next().step2().next()
interface Alarm {
    alert(): void;
}
class Door {}

class SecurityDoor extends Door implements Alarm {
    alert () {}
}

// 一个类可以继承多个接口
interface Light {
    lightOff(): void;
    lightOn(): void;
}

class Car implements Alarm, Light {
    alert () {}
    lightOff () {}
    lightOn () {}
}

// 接口继承接口
interface LightableAlarm extends Alarm {
    lightOn(): void
}

// 接口继承类
class Point {
    x: number
    y: number
    // private pp: number
    constructor (x: number, y: number) {
        this.x = x
        this.y = y    
    }
}

interface Point3D extends Point {
    z: number;
}

let p: Point3D = { x: 1, y: 1, z: 1 }

// 为什么接口可以继承类?
// 通过class声明类的时候除了创建一个类,还会创建一个类型
// Point除了可以当做类来使用,同样可以当做类型来使用

// Point当做类型使用,不包含构造函数和静态成员
// 声明合并
// 函数合并 -> 重载

interface Alarm {
  price: number;
  alert(): void;
}

interface Alarm {
  weight: number;
}

let alarm: Alarm = {
  price: 10,
  weight: 12,
  alert() { }
}

// 类合并同接口合并一样
// 接口中的方法与类型一样

interface Rect {
  width: number;
}
interface Rect {
  height: number;
}

let rect: Rect = {
  width: 100,
  height: 100
}

7. 字符串字面量和类型别名

// 类型别名
type Name = string
type NameResolver = () => string
type NameOrResolver = Name | NameResolver
function getName (x: NameOrResolver): string {
  if (typeof x === 'function') {
    return x()
  }
  return x
}

// 字符串字面量
// 字符串字面量用来约束取值只能是某几个字符串中的一个
type eventName = 'click' | 'mousemove' | 'scroll'
function handleEvent (ele: Element, event: eventName) {}

8. 泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。在函数内部使用泛型变量的时候,由于事先不知道他是哪种类型,所以不能随意操作它的属性或方法。这时,我们可以对泛型进行一些约束。
泛型类、泛型接口和泛型函数,泛型可以传入默认值。

// 不预先确定类型,具体的类型在使用的时候确定
// 泛型可用于类、函数和接口定义

function log<T> (value: T): T {
  console.log(value)
  return value
}

log('hello')
log([1, 2])

type Log = <T>(value: T) => T
const log1: Log = log

function createArray<T> (x: T, length: number): T[] {
  let result: T[] = []
  for (let i = 0; i < length; i++) {
    result.push(x)
  }
  return result
}

function swap<T, U> (tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]]
}

// 泛型约束接口
interface MyLog<T> {
  (value: T): T;
}

interface CreateArray<T> {
  (x: T, y: number): T[];
}

interface LengthWise {
  length: number;
}

function loggingIdentify<T extends LengthWise> (arg: T): number {
  return arg.length
}

// 函数和类可以轻松地支持多种类型,增强程序的可扩展性
// 不必写多条函数重载,冗长的联合类型声明,增强代码的可维护性
// 灵活地控制类型之间的约束

// 泛型类不能约束静态成员
class GenericNumber<T> {
  zeroNo: number;
  add: (x: T, y: T) => T
}

// 类型之间相互约束
function coypFields<T extends U, U> (target: T, source: U): T {
  for (let id in source) {
    target[id] = (source as T)[id]
  }
  return target
}

9. 内置对象

JavaScript中有很多内置对象,它们可以直接在TypeScript中当做定义好了的类型。内置对象是根据标准在全局作用域存在的对象。这里的标准是指ECMAScript和其他环境(比如DOM)的标准
ECMAScript标准提供的内置对象有:Boolean、Date、RegExp、Error等
BOM和DOM的内置对象有:Document、HTMLElement、Event、NodeList等
TypeScript核心库定义文件定义了浏览器所需用到的类型,并且预置在TypeScript中

// 内置对象
// ECMAScript内置对象
let e: Error = new Error('error')
let bool: Boolean = new Boolean(1)
let reg: RegExp = /[a-z]/
let date: Date = new Date()

// DOM内置对象
// Document、HTMLElement、Element、NodeList、Event
let body: HTMLElement = document.body
let lists: NodeList = document.querySelectorAll('li')
document.addEventListener('click', (e: MouseEvent) => {})

// 内置对象的定义文件都定义在TypeScript核心定义文件中

// node使用TypeScript,需要安装第三方定义文件@types/node

10. 类型断言

类型断言(TypeAssertion)可以用来指定一个值的类型

  • 联合类型可以被断言为一个具体的类型
  • 父类可以被断言为一个子类任意类型
  • 任意一个类型可以被断言为any
  • any类型也可以被断言为任意类型

并不是任意一个类型可以断言为任意一个类型
具体来说,若A类型兼容B类型,那么A类型可以被断言为B类型,B类型也可以被断言为A类型
Cat包含了Animal中所有属性,那么就可以看作Cat extends Animal,换言之就是说Animal兼容Cat(少的兼容多的)
当Animal兼容Cat时,那么他们就可以互相断言了
这样设计其实也很容易理解:
允许animal可以断言为Cat,是因为父类可以被断言为子类
允许cat断言为Animal,是因为既然子类拥有父类的属性和方法,那么断言为父类,获取父类的属性或调用父类的方法,就不会有任何问题,故子类可以被断言为父类

非空断言:x!将从x的值域中排除undefined和null
确定赋值断言,断言x会被赋值

let x!: number
console.log(x * 2)
function initialize () {
  x = 10
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值