ts基础

114 阅读6分钟

类型判断

  • 不设置类型会自动进行类型推论
// basic types
let isDone: boolean = false
let age: number = 10

let firstName: string = 'viking'
let message: string = `Hello, ${firstName}`

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

let num: string = null

let notSure: any = 4
notSure = 'maybe a string'
notSure = true

notSure.myName
notSure.getName()

// array
let arrOfNumbers: number[] = [1, 2, 3, 4]
arrOfNumbers.push(3)


// tuple
let user: [string, number] = ['viking', 20]
user = ['viking', 30]

// function
function add(x: number, y: number, z:number): number {
  return x + y
}
//let result = add(2, 3)
let add2 = (x: number, y:number): number => {
  return x + y
}
// 冒号后面等号前面都是声明类型
const add3:(x: number, y:number, z?: number) => number = add

const add4 = add2

我们发现当const add4 = add2也是可以的, add3会自动获得类型, 这种特性在 ts 中称为 类型推论(type inference)

ts会在初次没有指定类型的时候自动添加一个类型 又叫类型推论(type inference)

let str = 'str' // 类型推理为string
str = 122 // 这段代码爆红,因为类型推断为string 不能赋值 number

str = 122这段代码爆红,因为类型推断为string 不能赋值 number

对象类型判断 (interface)

interface是数据的共性的抽象 有自己的属性 只是在开发过程中做语法提示校验的工具 编译后不存在
interface接口的功能

  • 对对象的形状(shape)进行描述 (抽象的契约)
interface Person {
  readonly id: number; // readonly 只读属性 (不能修改)
  name: string;
  age?: number; // ? 参数可有可无
}

let viking: Person = {
  name: 'viking',
  age: 20,
  id: 1
}

const sum = (x: number, y: number) => {
  return x + y
}
interface ISum {
  (x: number, y: number): number
}
// 定义函数类型
const sum2: ISum = sum

interface RandomMap {
  [propName: string]: string; // 使用[] 去定义参数名的可变性
}
const test: RandomMap = {
  a: 'hello',
  b: 'test',
  c: 'test'
}
interface LikeArray {
  [index: number]: string
}
const likeArray: LikeArray = ['1', '2', '3']

// duck typing

interface FunctionWithProps {
  (x: number): number;
  name: string;
}
const a: FunctionWithProps = (x: number) => {
  return x
}
a.name = 'abc'

interface 这种模式(只要你有这个功能就适配) 又叫 duck typing

泛型

  • 诞生原因:
    • 类型推断 并不能推论出函数里参数的流向
    • 函数体内可能会做一些副作用的操作,想要 执行操作的时候在返回类型
  • 定义:范性是在定义函数、接口和类的时候不预先制定类型,而在使用的时候制定类型的一种特性(调用时定义类型)

范型定义在函数上

  1. 类型推断 并不能推论出函数里参数的流向

如下代码:定义一个函数,想通过传递参数不同返回对应的类型,类型为string, 但是 类型推断 并没有检测到, 导致 result 设置为number 并不会爆红

function echo(arg) {
  return arg
}
const result:number = echo('str') // echo('str')的返回类型是arg 
result.toFixed() // 这快ts语法检测不会检测到报错

范性给函数建立了一个通道,打通函数参数的类型,可以到函数内部,和它的输出,当遇到范型的时候,把调用时传递参数的类型自动流入函数体内 如下代码,将泛型的名称定义为<T>(T只是一个站位,在使用的时候指定)

function echo<T>(arg: T): T {
  return arg
}
const result = echo('str')// 指定为string
const test = echo(123)// 指定为number

范性传入多个值:

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

对象类型别名(type)

有些类型太长,所以需要定义个别名

let sum:(x: number, y: number) => number
type PlusType = (x: number, y: number) => number
let sum2: PlusType

交叉类型: 类型合并

interface IName {
  name: string
}
type IPerson = IName & { age: number } // & 合并
let person: IPerson = {name: 'hello', age: 12}

联合类型:xx类型 或者 xx类型

  • 当TS不确定一个联合类型的变量到底是什么的时候,只能访问 联合类型中共有的属性
let numberOrString: number | string
numberorString.length // 这快因为 string中有length 而 number 中没有所以报红

有时候需要在不确定类型的时候就访问其中类型的属性和方法,就需要类型断言

类型断言, 告诉ts比它更了解这个变量的类型,不让它爆红

function getLength(input: number | string) {
  const str = input as string
  if (str.length) {
    return str.length
  } else {
    const number = input as number
    return number.toString().length
  }
}
interface IName {
  name: string
}
type IPerson = IName & { age: number }
interface Person {
  name: string
  age: number
}
type PersonOptional = Partial<IPerson>// Partial 内置类型在后面高级特性讲
let viking2: PersonOptional = {}

范型定义在副作用上

  1. 函数体内可能会做一些副作用的操作,想要 执行操作的时候在返回类型

如下代码,在函数里调用请求接口,.then的时候 respary类型(不是想要的)

function withAPI(url: string) {
  return fetch(url).then(resp => resp.join())
}
withAPI('github.user').then(resp => { // 这块的resp 为 ary类型
})

需要如下改动,将 GithubResp 类型在 调用时传递通过 <T>(范型)流动

interface GithubResp {
  name: string;
  count: number
}
function withAPI<T>(url: string): Promist<T> {
  return fetch(url).then(resp => resp.join())
}
withAPI<GithubResp>('github.user').then(resp => { // 这块的resp 为 GithubResp
})

范型定义在接口上

代码如下

// import { FunctionComponent } from 'react'
// 原react类型定义
interface FunctionComponent<P = {}>{ // <P = {}> 是默认值的意思
  (props: PropsWithChildren<P>, content?:any): ReactElement<ary, ....>; // <P> 在调用的时候定义的类型
  propTypes?: WeakValidationMap<P>;
  contextTypes?: ValidationMap<any>;
  defaultProps?: Partial<P>;
  displayName?: string;
}
interface TestProps {
  title: string;
  desc: string;
}
const Test: FunctionComponent<TestProps> = (props) => {
  return (
    <>
      <h1>{props.title}</h1>
      <p>{props.desc}</p>
    </>
  )
}
Test.propTypes

高级特性

Partial源代码解析

  • keyof 字符串字面量(取键值)【知识点】 将一个interface全部变成可选
interface CountryResp {
  name: string;
  area: number;
  population: number;
}
 // keyof
type Keys = keyof CountryResp
// lookup types
type nameType = CountryResp['name']
// mapped types
type Test = {
  [key in Keys]: all
}
// 
type CountryOpt = {
  [p in Keys]?: CountryResp[p]
}

extends

  • extends场景:
    • 范型约束
    • extends并不是类上的继承概念, 在定义时并不知道入参的类型,arg.length不知道是否存在,所以爆红
function echoWithArr<T>(arg: T): T {
  console.log(arg.length) // 这块爆红,并不知道范型
  return arg
}
const arrs = echoWithArr([1,2,3])

所以,通过extends传来的参数取约束范型T 当前面表达式是面表达式的子级

interface IWithLength {
  length: number
}
function echoWithArr<T extends IWithLength>(arg: T): T {
  console.log(arg.length) // 这块爆红,并不知道范型
  return arg
}
const arrs = echoWithArr([1,2,3])
const str = echoWithArr('123')
const obj = echoWithArr({length: 123})

在TS2.8中负于了extends新用处,就是条件类型关键字
如果 T的子级有null或者undefined 则是 never 否则是 T

type NonType<T> = T extends null | undefined ? never : T
let demo1: NonType<number>
let demo1: NonType<null>

定义文件

假如是在项目被来是用ts写的,则不需要这个文件,库在tsc编译以后会生成 .d.ts 文件
单独写一般是应为原来是js模块,现在想让这个js模块在ts项目中使用。类似注释文件让ts读懂
ts默认加载node_model下的类型文件,所以需要把text.d.ts移入@types

// fetch.js
function myFetch(url, method, data) {
  return fetch(url, {
    body: data ? JSON.stringify(data) : '', 
    method
  }).then((resp) => resp.json())
}

myFetch.get = (url) => {
  return myFetch(url, 'GET')
}

myFetch.post = (url, data) => {
  return myFetch(url, 'POST', data)
}

export default myFetch
// 设置为字面量类型 根 keyof一样
type HTTPMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE'
// text.d.ts
declare function myFetch<T = any>(url: string, method: HTTPMethod, data?: any): Promise<T> {}
declare namespace myFetch{
  const get: <T = any>(url: string) => Promise<T>
  const post: <T = any>(url: string) => Promise<T>
}
export = myFetch //导出

调用

import myFetch from 'myFetch'
myFetch<String>('test', 'POST', {name: 'hello'}).then(data => { // data是string类型了

})
myFetch.get<number>('test').then(data => {

})