Typescript和React造轮子一之typescript基础(一)

135 阅读4分钟

1、变量类型

基础变量

let isDone: boolean = false
let age: number = 20
let binaryNumber: number = 0b1111
let firstName: string = 'viking'
let message: string = `Hello, ${firstName}, age is ${age}`
let u: undefined = undefined
let n: null = null
let num: number = undefined
let notSure: any = 4

联合类型

let numberOrString: number | string = 234
numberOrString = 'abc'

数组

let arrOfNumbers: number[] = [1, 2, 3, 4]
arrOfNumbers.push(5)
let user: [string, number] = ['viking', 1]

2、接口

interface Person {
  readonly id: number;
  name: string;
  age?: number;
}
let viking: Person = {
  id: 1234,
  name: 'viking',
}

3、函数及类型推断

// 函数声明
// function add(x: number, y: number, z: number = 10): number {
//   if (typeof z === 'number') {
//     return x + y + z
//   } else {
//     return x + y
//   }
// }

// let result = add(2, 3, 5)

const add = function(x: number, y: number, z: number = 10): number {
  if (typeof z === 'number') {
    return x + y + z
  } else {
    return x + y
  }
}

const add2: (x: number, y: number, z?: number) => number = add

4、类

类的使用和继承

class Animal {
  name: string;
  static categoies: string[] = ['mammal', 'bird']
  static isAnimal(a) {
    return a instanceof Animal
  }
  constructor(name: string) {
    this.name = name
  }
  run() {
    return `${this.name} is running`
  }
}

console.log(Animal.categoies)
const snake = new Animal('lily')
console.log(Animal.isAnimal(snake))

class Dog extends Animal {
  bark() {
    return `${this.name} is barking`
  }
}

const xiaobao = new Dog('xiaobao')

class Cat extends Animal {
  constructor(name) {
    super(name)
    console.log(this.name)
  }
  run() {
    return 'Meow, ' + super.run() 
  }
}

const maomao = new Cat('maomao')

类和接口

interface Radio {
  switchRadio(): void;
}

interface Battery {
  checkBatteryStatus();
}
// 继承接口
interface RadioWithBattery extends Radio {
  checkBatteryStatus();
}
class Car implements Radio{
  switchRadio() {

  }
}

class Cellphone implements RadioWithBattery {
  switchRadio() {

  }
  checkBatteryStatus() {

  }
}

5、枚举

const enum Direction {
  Up = 'UP',
  Down = 'DOWN',
  Left = 'LEFT',
  Right = 'RIGHT',
}
const value = 'UP'
if (value === Direction.Up) {
  console.log('go up!')
}

6、泛型

这里是难点也是重点,我会重点记录下 泛型的基本使用

function echo<T>(arg: T): T {
  return arg
}

const result = echo(true)

感悟: 函数名后声明的T其实是一个标记可以是任意符号,也不一定是函数中传进来的参数类型,只是为了约定参数和返回值是一种类型,这块如果不是很理解可以自习看看下面两个例子.

function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]]
}
const result2 = swap(['string', 123])

function echoWithArr<T>(arg: T[]): T[] {
  console.log(arg.length)
  return arg
}
const arrs = echoWithArr([1, 2, 3])

泛型和接口 下面例子就是使用接口来约束泛型,使传入的参数必须包含length属性

interface IWithLength {
  length: number
}

function echoWithLength<T extends IWithLength>(arg: T): T {
  console.log(arg.length)
  return arg
}

const str = echoWithLength('str')
const obj = echoWithLength({ length: 10, width: 10})
const arr2 = echoWithLength([1, 2, 3])

实例思考:

接下来我们再思考一个问题,我们现在创建一个数组队列如下:

class Queue {
  private data = [];
  push(item) {
    return this.data.push(item)
  }
  pop() {
    return this.data.shift()
  }
}

const queue = new Queue()
queue.push(1)
console.log(queue.pop().toFixed())

那么我们如何保证push进去的值一定是number,pop出来的值一定有toFixed函数???思考下

class Queue{
  private data = [];
  push(item: number) {
    return this.data.push(item)
  }
  pop(): number {
    return this.data.shift()
  }
}

const queue = new Queue()
queue.push(1)
console.log(queue.pop().toFixed())

我们在这里给输入的值使用了类型,返回值也添加了类型,这样就不会有任何问题了,这里我们就要再深入思考下了,如果我需要创造一个数组队列需要传字符串类型呢,,,,再写个新队列定义字符串? 其实可以不用那么麻烦,我们用泛型改造下


class Queue<T> {
  private data = [];
  push(item: T) {
    return this.data.push(item)
  }
  pop(): T {
    return this.data.shift()
  }
}

const queue = new Queue<number>()
queue.push(1)
console.log(queue.pop().toFixed())

const queue2 = new Queue<string>()
queue2.push('str')
console.log(queue2.pop().length)

这样我们就很轻松的解决了问题,只需要定义变量时传入泛型的类型就好了.

下面来看下变量和函数中泛型的使用:


interface KeyPair<T, U> {
  key: T;
  value: U;
}
let kp1: KeyPair<number, string> = { key: 123, value: "str" }
let kp2: KeyPair<string, number> = { key: 'test', value: 123 }

let arr: number[] = [1, 2, 3]
let arrTwo: Array<number> = [1, 2, 3]

interface IPlus<T> {
  (a: T, b: T) : T
}
function plus(a: number, b: number): number {
  return a + b;
}
function connect(a: string, b: string): string {
  return a + b
}
const a: IPlus<number> = plus
const b: IPlus<string> = connect

7、其他

类型别名

// type aliases
type PlusType = (x: number, y: number) => number
function sum(x: number, y: number): number {
  return x + y
}
const sum2: PlusType  = sum

type NameResolver = () => string
type NameOrResolver = string | NameResolver
function getName(n: NameOrResolver): string {
  if (typeof n === 'string') {
    return n
  } else {
    return n()
  }
}

类型断言

function getLength(input: string | number) : number {
  // const str = input as String
  // if (str.length) {
  //   return str.length
  // } else {
  //   const number = input as Number
  //   return number.toString().length
  // }
  if((<string>input).length) {
    return (<string>input).length
  } else {
    return input.toString().length
  }

8、声明文件

在typescript中如果要使用第三方库,ts可能并不知道库是什么东西,也不清楚类型这时候就会抛出错误给我们,接下来我们就拿jQuery来举例

declare var jQuery:(sel:string) => any

jQuery('.foot')

做了声明后我们就可以正常使用了,通常这个声明我们会单独放在一个文件里 jQyery.d.ts

declare var jQuery:(sel:string) => any

注意:声明文件必须是以d.ts为后缀的,一般情况下ts会去自动解析项目中所有的d.ts文件

当然有些情况下我们依然无法自动获取声明文件怎么办呢?这里就需要我们再ts配置文件中声明,tsconfig.json里我们可以写ts编辑器的各种配置

{
   "include": ["**/*"]
}

这下我们就解决了jQuery简单的声明了,但如果想正常使用jQuery中所有功能我们依然要声明很多内容,工作量真的很大... 其实社区早就为我们处理了很多常用的第三方库,我们只要搜索下载就可以使用了,这里是搜索地址