typeScript学习

文章目录

  • 执行命令
  • tsconfig.json
  • 类型
    • 基元类型:string、number、boolean
    • array
    • any
    • 变量上的类型注释
    • 函数
    • 对象类型
    • 联合类型union
    • 类型别名
    • 接口
    • 类型断言
    • 文字类型
    • null 和 undefined类型
    • 枚举
    • 不太常用的原语
  • 缩小
    • 类型缩小
    • typeof 类型守卫
    • 真值缩小
    • 等值缩小
    • in操作符缩小 确定对象是否具有某个名称的属性
    • instanceof 操作符缩小 检查一个值是否是另外一个值的实例
    • 分配缩小: 查看赋值的右侧并适当缩小左侧
    • 控制流分析
    • 使用类型谓词
    • 受歧视的unions 联合类型
    • never类型与穷尽性检查
  • 函数类型表达式
    • 调用签名
    • 构造签名
    • 泛型函数
    • 限制条件
    • 使用受限值
    • 指定类型参数
    • 编写优秀通用函数的准则
    • 可选参数
    • 回调中的可选参数
    • 函数重载
    • 重载签名和实现签名
    • 编写好的重载
    • 函数内的this声明
    • 其他类型
    • 参数展开运算符--形参展示
    • 参数展开运算符--实参展开
    • 参数解构
    • 返回void类型
  • 对象类型
    • 属性修改器
    • 扩展类型
    • 交叉类型
    • 接口与交叉类型
    • 泛型对象类型
  • 类型操纵
    • 从类型中创建类型
    • 泛型
    • 使用通用类型
    • 泛型类型
    • 泛型类
    • 泛型约束
    • 在泛型约束中使用类型参数
    • 在泛型中使用类类型
    • keyof类型操作符
    • typeof类型操作符
    • 索引访问类型
    • 条件类型
    • 条件类型约束
    • 在条件类型内推理
    • 分布式条件类型

执行命令

  • npm i -s typescript
  • npx tsc --init 生成tsconfig.json文件
  • npx tsc xxx.ts
  • npx tsc --watch
    自动编译,在报错时不进行自动编译
  • npx tsc --noEmitOnError --watch
  • node xx.js 执行对应文件

tsconfig.json

  1. 严格模式
  • “strict”: true
  • “noImplicitAny”: true
  • “strictNullChecks”: true
  1. 降级
  • “target”: “es2016”
  1. 输出的js文件
  • “outDir”: “./dist”
  1. ts所在文件
  • “rootDir”: “./src”,

类型

基元类型:string、number、boolean

let str:string= 'hello typescript'
let num:number = 100
let bool:boolean = true

array

let arr:number[] = [1,2,3]
// arr = ['a']
let arr2:Array<number> = [1,2,3]
// arr2=['a']

any

  • 不希望某个特定值导致类型检查错误 可以用任何类型的值
let obj:any={
  x:0
}
// let obj={
//   x:0
// }
// obj.foo()
// obj()
// obj.bar = 100
// obj = 'hello'
// const n:number = obj

变量上的类型注释

// let myName:string = 'Felixlu'
// int x = 0

let myName = 'Felixlu'
// myName = 100

函数

function greet(name:string){
  console.log("Hello," + name.toUpperCase() + "!!")
}
greet('aa')
function getFavoriteNumber():number{
  return 26
}

const names = ['小前','小封','小圆']
names.forEach(function(s) {
  console.log(s.toUpperCase())
})
names.forEach((s)=> {
  console.log(s.toUpperCase)
})

对象类型

function printCoord(pt:{x:number;y:number}){
  console.log("x:" + pt.x)
  console.log("y:" + pt.y)
}
printCoord({x:3, y:7})

function printName(obj: {first:string, last?: string}){
  // console.log(obj.last?.toUpperCase())
  // if(obj.last != undefined){
  //   console.log(obj.last.toLowerCase())
  // }
  // ?解决last是undefined的问题
  console.log(obj.last?.toUpperCase())
}
// printName({
//   first:'Felix'
// })
// printName({
//   first:'Felix',
//   last:'Lu'
// })
printName({
  first:'Felix',
  last:undefined
})

联合类型union

  • let id: number | string
  • 两个或多个其他类型组成的类型
function printId7(id:number | string){
  // console.log('Your ID is:' +id)
  // console.log(id.toUpperCase())
  if(typeof id === 'string'){
    console.log(id.toUpperCase())
  }else{
    console.log(id)
  }
}

printId7(101)
printId7('202')
// printId7(true)

function welcomPeople(x:string[] | string){
  if(Array.isArray(x)){
    console.log('Hello,' + x.join('and'))
  }else{
    console.log('Welcome lone traveler' + x)
  }
}

welcomPeople('A')
welcomPeople(['a','b'])

function getFirstThree(x:number[] | string): number[] | string {
  return x.slice(0,3)
}

console.log(getFirstThree('abcdefg'))
console.log(getFirstThree([2,3,4,5,6]))

类型别名

  • 给类型定义一个名字,用这个名字来代替定义的类型
type Point = {
  x:number
  y:number
}
function printCoord8(pt:Point) {

}
printCoord8({x:100,y:200})

type ID = number | string
function printId8(id:ID){

}
printId8(100)
printId8('10')

type UserInputSanitizedString = string
function sanitizedInput(str:string): UserInputSanitizedString {
  return str.slice(0,2)
}
let userInput = sanitizedInput('hello')
console.log(userInput)
userInput = 'new Input'

接口

一种结构类型,定义对象类型的另外一种方式 用关键字interface来定义

interface Point9{
  x:number;
  y:number;
}
function printCoord9(pt:Point9){
  console.log("坐标x的值是:"+pt.x)
  console.log("坐标y的值是:"+pt.y)
}
printCoord9({x:100,y:100})

// 扩展接口
/* interface Animal{
  name:string
}
interface Bear extends Animal{
  honey:boolean
}
const bear:Bear = {
  name:'winie',
  honey: true
}
console.log(bear.name)
console.log(bear.honey) */

/* type Animal = {
  name:string
}
type Bear = Animal & {
  honey:boolean
}
const bear:Bear = {
  name:'winnie',
  honey:true
} */

// 向现有的类型添加字段
/* interface MyWindow {
  count:number
}
interface MyWindow {
  title:string
}
const w: MyWindow = {
  title:'hello ts',
  count:100
} */

// 类型type创建后不能更改, interface可新增
/* type MyWindow = {
  title:string
}
type MyWindow = {
  count:string
} */

类型断言

  • 指定一个更具体的类型 类型断言由编译器来删除
  1. const myCanvas = document.getElementById(“main_canvas”) as HTMLCanvasElement
  2. const myCanvas = document.getElementById(“main_canvas”)
const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement
const myCanvas2 = <HTMLCanvasElement>document.getElementById("main_canvas")

const x = ('hello' as any) as number
const y = ('hello' as unknown) as number

文字类型

let b2:false = false

const obj11 = {
  count:0
}
if(true){
  obj11.count = 1
}

function handleRequest(url:string, method: 'GET'|'POST'|'GUESS'){}
/* const req = {
  url:'https://example.com',
  method:'GET' // 1 method:'GET' as 'GET'
}
// handleRequest(req.url, req.method) // Argument of type 'string' is not assignable to parameter of type '"GET" | "POST" | "GUESS"'
handleRequest(req.url, req.method as 'GET') // 2 */
const req = {
  url:'https://example.com',
  method:'GET'
} as const  // 3
// handleRequest(req.url, req.method) // Argument of type 'string' is not assignable to parameter of type '"GET" | "POST" | "GUESS"'
handleRequest(req.url, req.method) // 2

null 和 undefined类型

null 不存在
undefined 未初始化的值

let x12:undefined = undefined
let y12:null = null
// let z12:string = undefined // Type 'undefined' is not assignable to type 'string'.  // 可将tsconfig.josn文件"strictNullChecks"改为false,就可行

function doSomething(x:string | null){
  if(x=== null){
    // 做一些事情
  }else {
    console.log('Hello,' + x.toUpperCase())
  }
}

function liveDangerously(x?:number | null){
  // !断言为你能肯定这个值不是null或者undefined
  console.log(x!.toFixed())
}

枚举

enum Direction{
  Up = 1,
  Down,
  Left,
  Right,
}
console.log(Direction.Up) // 1

不太常用的原语

bigint 非常大的整数
symbol 全局唯一引用

// BigInt literals are not available when targeting lower than ES2020. 修改"target": "es2020",
const oneHundred: bigint = BigInt(100)
const anotherHundred: bigint = 100n

const firstName = Symbol("name")
const secondName = Symbol("name")

// This comparison appears to be unintentional because the types 'typeof firstName' and 'typeof secondName' have no overlap.
// if(firstName === secondName){
//   console.log('ok')
// }

缩小

类型缩小

宽类型转为窄类型过程,常用于处理联合类型变量的场景

function padLeft(padding:number | string, input: string): string {
  <!-- throw new Error("尚未实现!") -->
  return new Array(padding + 1).join(" ") + input
}
function padLeft(padding:number | string, input: string){
  if(typeof padding === "number"){
    return new Array(padding + 1).join(" ") + input
  }
  return padding + input
}
function printAll(strs:string | string[] | null){
  // 'strs' is possibly 'null' 因为typeof null == object
  if(typeof strs === 'object'){
    // for(const s of strs){
    //   console.log(s)
    // }
  }else if(typeof strs === 'string'){
    console.log(strs)
  } else {
    // ...
  }
}

typeof 类型守卫

typeof strs === “object” // “string”、“number”,“bigint”,“boolean”,“symbol”,“undefined”,“function”

function printAll2(strs:string | string[] | null){
  // 'strs' is possibly 'null' 因为typeof null == object
  /* if(strs && typeof strs === 'object'){
    for(const s of strs){
      console.log(s)
    }
  }else if(typeof strs === 'string'){
    console.log(strs)
  } else {
    // ...
  } */
  if(strs){
    if(typeof strs === 'object'){
      for(const s of strs){
        console.log(s)
      }
    }else if(typeof strs === 'string'){
      console.log(strs)
    } else {
      // ...
    }
  }
}

function multiplyAll(
  values:number[] | undefined,
  factor:number
){
  if(!values){
    return values
  }else{
    return values.map((x) => {
      return x*factor
    })
  }
}
console.log(multiplyAll([3,4],2))
console.log(multiplyAll(undefined,2))
multiplyAll([3,4],2)

真值缩小

条件、&&、||、if语句、布尔否定( !)
function getUsersOnlineMessage (numUsersOnline:number){

if(numUsersOnline){
return 现在共有 ${numUsersOnline} 人在线!
}
return “现在没有人在线. ”
}

Boolean(“hello”) // type:boolean,value:true
!!“world” // type:true, value:true

等值缩小

=== , !==, ==, !=

function example3(x:string | number, y: string | boolean){
  // x.toUpperCase() //  Property 'toUpperCase' does not exist on type 'string | number'.
  // y.toUpperCase()
  if(x===y){
    x.toUpperCase()
    y.toUpperCase()
  }else{
    console.log(x)
    console.log(y)
  }
}

function printAll3(strs:string | string[] | null){
  if(strs !== null){
    if(typeof strs === 'object'){
      for(const s of strs){
        console.log(s)
      }
    }else if(typeof strs === 'string'){
      console.log(strs)
    } else {
      // ...
    }
  }
}

interface Container {
  value: number | null | undefined
}

function multiplyValue(container: Container, factor: number) {
  if(container.value != null){
    console.log(container.value)
    container.value *= factor
  }
}

multiplyValue({value:5},6)
multiplyValue({value:undefined},6)
multiplyValue({value:null},6)
// multiplyValue({value:'5'},6)

in操作符缩小 确定对象是否具有某个名称的属性

value in x

  • 为true x具有可选或必须属性的类型的值
  • 为false x需要具有可选或缺失属性的类型的值
type Fish = { swim: () => void}
type Bird = { fly: () => void}
type Human = { swim?: () => void; fly?: () => void}

// function move (animal: Fish | Bird){
//   if('swim' in animal){
//     return animal.swim()
//   }

//   return animal.fly()
// }
function move (animal: Fish | Bird | Human){
  // Cannot invoke an object which is possibly 'undefined'.
  if('swim' in animal){
    // return animal.swim()
    return (animal as Fish).swim()
  }

  // return animal.fly()
  return (animal as Bird).fly()
}

instanceof 操作符缩小 检查一个值是否是另外一个值的实例

x instanceof Foo
x的原型链上是否含有Foo.prototype

function logValue(x:Date | string){
  if(x instanceof Date){
    console.log(x.toUTCString())
  }else{
    console.log(x.toUpperCase())
  }
}

logValue(new Date())
logValue('hello ts')

分配缩小: 查看赋值的右侧并适当缩小左侧

// let x: string | number
let x = Math.random() < 0.5 ? 10 : “hello world!”

// let x: string | number
let x6 = Math.random() < 0.5 ? 10 : "hello world!"

// let x6 : number
x6 = 1
console.log(x6)

// let x6 = string
x6 = 'goodbye!'
console.log(x6)

// let x6: boolean
// x6 = true // Type 'boolean' is not assignable to type 'string | number'.

控制流分析

function padLeft(padding:number | string, input:string){
if(typeof padding === “number”){
return new Array(padding + 1).join(" ") + input
}
return padding + input
}

function example7(){
  let x: string | number | boolean

  x = Math.random() < 0.5
  // let x: boolean
  console.log(x)

  if(Math.random() < 0.5){
    x = 'hello'
    // x:string
    console.log(x)
  }else{
    x = 100
    // x: number
    console.log(x)
  }
  // x变成了string|number,boolean被覆盖了
  return x
}

let x = example7()
x = 'hello'
x = 100
// 不能将类型“boolean”分配给类型“string | number”
// x = true

使用类型谓词

直接控制整改代码的类型变化,定义一个用户定义的类型保护,直接定义一个函数,让它返回值是一个类型谓词就好了
function isFish(pet: Fish | Bird): pet is Fish{
return (pet as Fish).swim !== undefined

}
返回值: pet is Fish 类型谓词

type Fish8 = {
  name: string
  swim: () => void
}

type Bird8 = {
  name:string
  fly:() => void
}

function isFish(pet: Fish8 | Bird8): pet is Fish8{
  return (pet as Fish8).swim !== undefined
}

function getSmallPet(): Fish8 | Bird8 {
  let fish: Fish8 = {
    name: 'sharkey',
    swim:()=> {

    }
  }

  let bird: Bird8 = {
    name:'sparrow',
    fly: ()=> {

    }
  }
  return true ? bird : fish
}
let pet = getSmallPet()
if(isFish(pet)){
  pet.swim()
}else{
  pet.fly()
}

const zoo: (Fish8 | Bird8)[] = [getSmallPet(), getSmallPet(), getSmallPet()]
const underWater1: Fish[] = zoo.filter(isFish)
const underWater2: Fish[] = zoo.filter(isFish) as Fish[]

const underWater3: Fish[] = zoo.filter((pet): pet is Fish8 => {
  if(pet.name === 'frog'){
    return false
  }
  return isFish(pet)
})

受歧视的unions 联合类型

联合类型出现的问题以及解决
interface Shape {
kind: “circle” | “square”
radius?: number
sideLength?:number
}

// interface Shape {
//   kind: "circle" | "square"
//   radius?: number
//   sideLength?:number
// }

interface Circle {
  kind:'circle'
  radius: number
}
interface Square {
  kind:'square'
  sideLength: number
}

type Shape = Circle | Square

/* function handleShape(shape:Shape) {
  if(shape.kind === 'rect'){

  }
} */

/* function getArea(shape:Shape){
  if(shape.kind === 'circle'){
    return Math.PI * shape.radius! ** 2
  }
} */

function getArea(shape:Shape){
  switch(shape.kind){
    case 'circle':
      return Math.PI * shape.radius ** 2
    case 'square':
      return shape.sideLength ** 2
  }
}

never类型与穷尽性检查

never:不应该存在的状态

interface Circle {
  kind:'circle'
  radius: number
}
interface Square {
  kind:'square'
  sideLength: number
}
interface Triangle {
  kind:'triangle'
  sideLength: number
}

type Shape10 = Circle | Square | Triangle

function getArea10(shape:Shape10){
  switch(shape.kind){
    case 'circle':
      return Math.PI * shape.radius ** 2
    case 'square':
      return shape.sideLength ** 2
    default:
      // 不能将类型“Triangle”分配给类型“never”,除非再加一个 case ‘Triangle’
      const _exhaustiveCheck: never = shape
      return _exhaustiveCheck
  }
}

函数类型表达式

fn: (a:string) => void

type GreetFunction = (a:string) => void
function greeter(fn: GreetFunction){
  fn('Hello')
}

function printToConsole(s:string){
  console.log(s)
}

greeter(printToConsole)

调用签名

type DescribableFunction = {
  description: string;
  (someArg: number): boolean;
}
function doSomething(fn: DescribableFunction) {
  console.log(fn.description + ' returned ' + fn(6))
}

function fn1(n:number){
  console.log(n)
  return true
}
fn1.description = 'hello'

doSomething(fn1) // 6    'hello returned true'

构造签名

// 参数s,返回对象Ctor,new构造签名
// class Ctor {
//   s:string
//   constructor(s:string){
//     this.s = s
//   }
// }

// /* type SomeConstructor = {
//   (s:string):Ctor
// }

// function fn(ctor: SomeConstructor){
//   ctor('hello')
// } */

// type SomeConstructor = {
//   new (s:string):Ctor
// }

// function fn(ctor: SomeConstructor){
//   return new ctor('hello')
// }

// const f = fn(Ctor)
// console.log(f.s)

// 调用签名和构造签名
interface CallOrConstructor {
  new (s:string):Date
  (n?: number):number
}

function fn(date:CallOrConstructor){
  let d = new date('2021-12-20')
  let n = date(100)
}

泛型函数

两个值之间存在对应关系

function firstElement(arr: any[]){
  return arr[0]
}
// 标识符 入参为标识符Type类型 返回值类型也是Type
function firstElement<Type>(arr:Type[]):Type | undefined{
  return arr[0]
}
const s = firstElement(["a",'b','c']) // s是'string'类型
const n = firstElement([1,2,3]) // n是'number'类型
const u = firstElement([]) // n是undefined类型
/* function firstElement(arr: any[]){
  // 返回值return决定,arr由any值组成的数组
  // return arr[0] // 返回any
  return 100 // 返回number
}
const s = firstElement(["a",'b','c']) */

// 让输入和输出类型保持一致
function firstElement<Type>(arr: Type[]): Type | undefined{
  return arr[0]
  // return 100
}
firstElement(['a','b','c'])
firstElement([1,2,3])
firstElement([])
firstElement<string>(['a','b','c'])
firstElement<number>([1,2,3])
firstElement<undefined>([])

function map<Input,Output>(arr: Input[], func:(arg: Input) => Output): Output[]{
  return arr.map(func)
}
const parsed = map(['1','2','3'], (n)=> parseInt(n))
console.log(parsed) // [ 1, 2, 3 ]

限制条件

约束条件限制一个类型参数可以接受的类型
为了保证Type一定有一个length类型,所以加一个限制条件,给这个泛型后面加关键字extends { length: number}

function longest<Type extends { length: number}>(a: Type, b: Type){
  if(a.length >= b.length){
    return a
  }else{
    return b
  }
}
/* function longest(a: Type, b: Type){
  // Property 'length' does not exist on type 'Type'
  if(a.length >= b.length){
    return a
  }else{
    return b
  }
} */
function longest<Type extends { length: number}>(a: Type, b: Type){
  if(a.length >= b.length){
    return a
  }else{
    return b
  }
}
const longerArray = longest([1,2],[1,2,3])
const longerString = longest('fleee','ss')
// extends { length: number}是限制条件而不是扩展条件
// const notOk = longest(10,100) // Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.

使用受限值

function minimumLength<Type extends {length:number}>(obj:Type,minimum:number):Type{
  if(obj.length >= minimum){
    return obj
  }else{
    return {length:minimum}
  }
}
function minimumLength<Type extends {length:number}>(
  obj:Type,
  minimum:number
  ):Type{
  if(obj.length >= minimum){
    return obj
  }else{
    return {length:minimum} // 不能将类型“{ length: number; }”分配给类型“Type”。"{ length: number; }" 可赋给 "Type" 类型的约束,但可以使用约束 "{ length: number; }" 的其他子类型实例化 "Type"。
  }
}
const arr = minimumLength([1,2,3], 6)
console.log(arr.slice(0))

指定类型参数

const arr = combine([1,2,3], [‘hello’])

function combine<Type>(arr1: Type[], arr2: Type[]): Type[]{
  return arr1.concat(arr2)
}

// const arr = combine([1,2,3],[4,5])
// ts会自动推断type类型
// const arr = combine([1,2,3],['asss','5']) //Type 'string' is not assignable to type 'number'.
// 指定type类型为string或者number
const arr = combine<string | number>([1,2,3],['asss','5'])
console.log(arr)

编写优秀通用函数的准则

  1. 可能的情况下,使用类型参数本身,而不是对其进行约束
  2. 总是尽可能少地使用类型参数
  3. 如果一个类型的参数只出现在一个地方,请重新考虑你是否真的需要它
// 1. 可能的情况下,使用类型参数本身,而不是对其进行约束
function firstElement1<Type>(arr: Type[]){
  return arr[0]
}
function firstElement2<Type extends any[]>(arr: Type){
  return arr[0]
}

const a = firstElement1([1,2,3])
const b = firstElement2([1,2,3])

// 2. 总是尽可能少地使用类型参数
function filter1<Type>(arr: Type[], func:(arg:Type) => boolean){
  return arr.filter(func)
}
function filter2<Type,Func extends(arg:Type)=>boolean>(
  arr:Type[],
  func:Func
){
  return arr.filter(func)
}

// 3. 如果一个类型的参数只出现在一个地方,请重新考虑你是否真的需要它
function greet<Str extends string>(s:Str){
  console.log('hello'+s)
}
greet('ssss')
function greet2(s:string){
  console.log('hello'+s)
}
greet2('ddd')

可选参数

可变数量的参数

function f(n:number){
  console.log(n.toFixed()) // 0个参数
  console.log(n.toFixed(3)) // 1个参数
}
// ?标记这个参数可以有也可以没有
function f(x?:number){

}
f()
f(10)
function f(n:number = 100){
  console.log(n.toFixed()) // 0个参数
  console.log(n.toFixed(3)) // 1个参数
}
f(123.45)
f() // 不传参,n.toFixed会报错,所以赋一个默认值

回调中的可选参数

当为回调写一个函数类型时,永远不要 写一个 可选参数,除非你打算在不传递该参数的情况下调用函数

function myForEach(arr: any[], callback:(arg:any, index?: number) => void){
  for(let i = 0; i<arr.length; i++){
    // callback(arr[i], i)
    callback(arr[i])
  }
}

// myForEach([1,2,3],(a)=> console.log(a))
// myForEach([1,2,3],(a,i)=> console.log(a,i))
// 1 2 3    1 0   2 1   3 2

// i为可选参数
myForEach([1,2,3],(a,i)=> {
  // 'i' is possibly 'undefined'.
  // console.log(i.toFixed())
})

函数重载

指定重载签名可以不同方式调用函数
函数签名一般是两个或者多个

function makeDate(timestamp: number): Date;
function makeDate(m:number,d:number,y:number):Date;
function makeDate(mOrTimestamp:number,d?:number,y?:number):Date{

};
// 重载签名
function makeDate(timestamp: number): Date;
function makeDate(m:number,d:number,y:number):Date;
// 实现签名,实现函数的参数必须对重载函数的参数保持一致
function makeDate(mOrTimestamp:number,d?:number,y?:number):Date{
  if(d!==undefined && y!==undefined){
    return new Date(y,mOrTimestamp,d)
  }else{
    return new Date(mOrTimestamp)
  }
};

const d1 = makeDate(12345678)
const d2 = makeDate(5,6,7)
// No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments. 没有需要 2 参数的重载,但存在需要 1 或 3 参数的重载
// const d3 = makeDate(5,6)

重载签名和实现签名

问题

  • 参数不正确
  • 参数类型不正确
  • 返回类型不正确
/* function fn12(x:string):void
function fn12(){

}
fn12('1') */

/* function fn12(x:boolean):void
function fn12(x:string):void
// 此重载签名与其实现签名不兼容
// function fn12(x:boolean){
function fn12(x:boolean | string){

} */

function fn12(x:boolean):string
function fn12(x:string):boolean
function fn12(x:boolean | string): string | boolean{
  return 'hello'
}

编写好的重载

在可能的情况下,总是倾向于使用联合类型的参数而不是重载参数

/* function len(s:string):number
function len(arr:any[]):number
function len(x:any){
  return x.length
}
len('hello')
len([1,2,3])
//  Argument of type '"hello" | number[]' is not assignable to parameter of type 'any[]'.
len(Math.random() > 0.5 ? 'hello':[1,2,3]) */

function len(x:any[] | string){
  return x.length
}
len('hello')
len([1,2,3])
len(Math.random() > 0.5 ? 'hello':[1,2,3])

函数内的this声明

ts通过代码流分析推断函数中的this应该是什么

const user = {
  id: 123,
  admin:false,
  becomeAdmin: function(){
    this.admin = true
  }
}
interface DB {
  filterUsers(filter:(this:User) => boolean): User[]
}
interface User {
  admin:boolean
}

interface DB {
  filterUsers(filter: (this:User)=>boolean): User[]
}

const db:DB = {
  filterUsers:(filter: (this: User)=> boolean)=>{
    let user1:User = {
      admin:true
    }

    let user2:User = {
      admin:false
    }

    return [user1, user2]
  }
}

// 这个function不能是箭头函数
const admins = db.filterUsers(function(this:User){
  return this.admin
})
// const admins1 = db.filterUsers((this:User)=>{
//   return this.admin
// })
console.log(admins) // [ { admin: true }, { admin: false } ]

其他类型

  1. void 没有返回值函数的返回值
    function noop(){return;} // 默认推断出返回void
  2. object指的是任何的不是基元的值:string number bigint boolean symbol null undefined
    不同于{},不同于Object
  3. unknown类型代表任何值,这与any类型类似,但更安全,因为对未知unknow你、值做任何事情都是不合法的
  4. never 永远不会被观察到的值,返回值抛出异常,或终止程序执行或死循环
  5. Function 全局性的Function类型描述了诸如bind、call、apply和其他存在于JavaScript中所有函数值的属性,它还有一个特殊的属性,即Function类型的值总是可以被调用,这些调用返回any
function f1(a:any){
  a.b() // 正确
}
function f2(a:unknown){
  a.b()
}

function safeParse(s:string):unknown{
  return JSON.parse(s)
}
const obj = safeParse(someRandomString)

function fail(msg:string):never{
  throw new Error(msg)
}
function fn(x:string|number){
  if(typeof x === 'string'){

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

  }else{
    x; // ‘never’ ,这里永远不会被触发
  }
}
function doSomething(f:Function){
  return f(1,2,3) // 返回值为any,应避免这样做,因为any不安全
}
// 不打算调用就用=> void

参数展开运算符–形参展示

function multiply(n: number, ...m: number[]){
  return m.map((x)=> n*x)
}
const a = multiply(10, 1,2,3,4) // 10是n,1234被m接收形成数组
// [ 10, 20, 30, 40 ]

参数展开运算符–实参展开

const arr1 = [1,2,3]
const arr2 = [4,5,6]
arr1.push(...arr2)

/* const args = [8,5]
const angle = Math.atan2(...args) //扩张参数必须具有元组类型或传递给 rest 参数 */
const args = [8,5] as const
const angle = Math.atan2(...args) //扩张参数必须具有元组类型或传递给 rest 参数

参数解构

function sum({a,b,c}:{a:number;b:number,c:number}){
  console.log(a+b+c)
}
sum({a:10,b:3,c:9}) // 22

返回void类型

一个具有void返回类型的上下文函数类型(type vf = () => void)在实现时,可以返回任何其他的值,但它会被忽略
当一个字面的函数定义一个void的返回类型时,该函数必须不返回任何东西

type voidFunc = () => void
const f1:voidFunc = () => {
  return true
}
const f2:voidFunc = () => true
const f3:voidFunc = function(){
  return true
}

const v1:void = f1()
const v2 = f2()
const v3 = f3()

function f4():void{
  return true
}

const f5 = function(): void{
  return true
}

对象类型

匿名对象 接口命名 类型别名

// 匿名对象
/* function greet(person:{name:string, age:number}){
  return 'Hello' + person.name
} */
// 接口命名
/* interface Person {
  name:string
  age:number
}

function greet(person:Person){
  return 'Hello' + person.name
} */
// 类型别名
type Person01 = {
  name:string
  age:number
}

function greet(person:Person){
  return 'Hello' + person.name
}

属性修改器

  1. 可选属性
type Shape = {}
interface PaintOptions{
  shape:Shape,
  xPos?:number,
  yPos?:number
}

/* function paintShape(opts:PaintOptions){
  let xPos = opts.xPos
  let yPos = opts.yPos
  console.log(xPos)
} */
/* function paintShape(opts:PaintOptions){
  let xPos = opts.xPos === undefined ? 0:opts.xPos
  let yPos = opts.yPos === undefined ? 0:opts.yPos
  console.log(xPos)
} */
// 解构
/* function paintShape({shape, xPos = 0, yPos = 0}:PaintOptions){
  // let xPos = opts.xPos === undefined ? 0:opts.xPos
  // let yPos = opts.yPos === undefined ? 0:opts.yPos
  console.log(xPos)
} */
function paintShape({shape:Shape, xPos:number = 0, yPos = 0}:PaintOptions){
  console.log(Shape)
  console.log(number)
}

const shape:Shape={}
paintShape({shape})
paintShape({shape,xPos:100})
paintShape({shape,yPos:100})
paintShape({shape,xPos:100,yPos:100})
  1. 只读属性:不能被写入其他值
interface SomeType{
  readonly prop:string
}
function doSomething(obj:SomeType){
  console.log(obj.prop)
  // obj.prop = 'hello' // 无法为“prop”赋值,因为它是只读属性
}

interface Home {
  readonly resident:{
    name:string
    age:number
  }
}
/* interface Home {
  readonly resident:{
    name:string
    readonly age:number
  }
} */
function visitForBirthday(home:Home){
  console.log(home.resident.name)
  home.resident.age++
}
visitForBirthday({resident:{name:'11',age:11}})
function evict(home:Home){
  // home.resident = {// 无法为“resident”赋值,因为它是只读属性
  //   name:'ss',
  //   age:15
  // }
}

// readonly属性可以通过别名改变
interface Person{
  name:string
  age:number
}
interface ReadonlyPerson{
  readonly name:string
  readonly age:number
}
let writablePerson:Person = {
  name:'Felix',
  age:18
}
let readonlyPerson:ReadonlyPerson = writablePerson

console.log(readonlyPerson.age)//18
writablePerson.age++
console.log(readonlyPerson.age)//19
  1. 索引前面:描述可能的值的类型
interface StringArray{
  [index: number]:string
}
const myArray:StringArray = ['a','b']
const secondItem = myArray[0]

interface TestString{
  [props:string]:number
}
let testString:TestString = {
  x:100,
  y:100,
  // aaa:'aa'
}

interface Animal{
  name:string
}
interface Dog extends Animal{
  breed:string
}
interface NotOkay{
  [index:string]:number | string
  length:number
  name:string
}
let notOkay:NotOkay = {
  x:100,
  length:100,
  name:'ss'
}

interface ReadonlyStringArray{
  readonly [index:number]:string
}
let myArray2:ReadonlyStringArray = ['a','b']
// myArray2[0] = 'aaa' //类型“ReadonlyStringArray”中的索引签名仅允许读取。

扩展类型

interface BasicAddress{
}
interface AddressWithUnit extends BasicAddress {
  unit:string
}
interface BasicAddress{
  name?:string
  street:string
  city:string
  country:string
  postalCode:string
}
/* interface AddressWithUnit{
  name?:string
  street:string
  city:string
  country:string
  postalCode:string
  unit:string
} */

interface AddressWithUnit extends BasicAddress{
  unit:string
}
let awu:AddressWithUnit = {
  unit:'3单元',
  street:'ssss',
  city:'xxxx',
  country:'qqq',
  postalCode:'wree',
  name:'v'
}

interface Colorful{
  color:string
}
interface Circle{
  radius:number
}
interface ColorCircle extends Colorful, Circle{

}
const cc05: ColorCircle = {
  color:'red',
  radius:100
}

交叉类型

type ColorfulCircle = Colorful & Circle

interface Colorful{
  color:string
}
interface Circle{
  radius:number
}
type ColorfulCircle = Colorful & Circle
const cc: ColorfulCircle = {
  color:'cc',
  radius:100
}
function draw(Circle:Colorful & Circle){
  console.log(Circle.color)
  console.log(Circle.radius)
}
draw({
  color:'red',
  radius:100
})

接口与交叉类型

如何处理冲突

  1. 类型合并使用interface
  2. 避免类型冲突用type
// 类型合并使用interface
/* interface Sister{
  name:string
}
interface Sister{
  age:number
}
const sister1:Sister = {
  name:'xx',
  age:20
} */
// 避免类型冲突用type
type Sister = {
  name:string
}
type Sister = { // 标识符“Sister”重复
  name:string
}

泛型对象类型

interface Box{
  content:any;
}
interface Box{
  content:unknown;
}
interface Box<Type>{
  contents:Type
}
let box:Box<string>
/* interface Box{
  contents:any
}
let box: Box = {
  contents:'hello'
} */

/* interface Box{
  contents:unknown
}
let x: Box={
  contents:'hello world'
}
if(typeof x.contents === 'string'){
  console.log(x.contents.toLowerCase())
}
// console.log(x.contents.toLowerCase()) //“x.contents”的类型为“未知”
console.log((x.contents as string).toLowerCase()) */

/* interface NumberBox{
  contents:number
}
interface StringBox{
  contents:string
}
interface BooleanBox{
  contents:boolean
}

function setContents(box:StringBox,newContents:string):void
function setContents(box:NumberBox,newContents:number):void
function setContents(box:BooleanBox,newContents:boolean):void
function setContents(box:{contents:any},newContents:any){
  box.contents = newContents
} */

/* interface Box{
  contents:Type
}
interface StringBox{
  contents:string
}
let boxA: Box = {
  contents:100
}
let boxB:StringBox={
  contents:100
}
 */

/* interface Box{
  contents:Type
}
interface Apple{

}
let a:Apple={}
type AppleBox = Box
let ab:AppleBox = {
  contents:a
}
 */

// 类型别名
type Box<Type> = {
  contents:Type
}
type OrNull<Type> = Type | null
type OneOrMany<Type> = Type | Type[]
type OneOrManyOrNull<Type> = OrNull<OneOrMany<Type>>
type OneOrManyOrNullString = OneOrManyOrNull<string>

类型操纵

从类型中创建类型

  1. 泛型类型:带有参数的类型
  2. keyof类型操作符创建新的类型
  3. typeof类型操作符
  4. 索引访问类型
  5. 条件类型
  6. 映射类型
  7. 模板字面量类型

泛型

创建一个在各种类型上工作的组件

function identity(arg:number):number{
  return arg
}
// any丢失类型信息
function identity(arg:any):any{
  return arg
}
// 需要一种方法来捕获参数类型,以便可以用它来表示返回的内容
// 类型变量,只对类型起作用而不是数值
// 不会丢失任何类型信息
function identity<Type>(arg:Type):Type{
  return arg
}
let output = identity<string>("myString")
// 类型参数推断
let output = identity("myString")

使用通用类型

function loggingIdentity<Type>(arg:Type):Type{
  console.log(arg.length)
  return arg
}
// Type是个泛型
// function loggingIdentity(arg:Type):Type{
//   console.log(arg.length) // 类型“Type”上不存在属性“length” 解决:规定某个类型
//   return arg
// }
function loggingIdentity1<Type>(arg:Array<Type>):Type[]{
  console.log(arg.length) // 类型“Type”上不存在属性“length” 解决:规定某个类型
  return arg
}
// loggingIdentity('hello') // 5
loggingIdentity1([100,200]) // 2

泛型类型

interface GenericIdentityFn{
  <Type>(arg:Type):Type;
}
function identity<Type>(arg:Type):Type{
  return arg
}
let myIdentity: <Type>(arg:Type)=> Type = identity
let myIdentity2: <Input>(arg:Input)=> Input = identity
let myIdentity3: {<Type>(arg:Type): Type} = identity

// 泛型接口
interface GenericIdentityFn{
  <Type>(arg:Type):Type
}
let myIdentity4: GenericIdentityFn = identity

interface GenericIdentityFn2<Type>{
  (arg:Type):Type
}
// let myIdentity5: GenericIdentityFn2 = identity // 泛型类型“GenericIdentityFn2”需要 1 个类型参数。
let myIdentity5: GenericIdentityFn2<string> = identity

泛型类

类的名称后面加尖括号

class GenericNumber<NumType>{
  zeroValue:NumType;
  add:(x:NumType, y:NumType) => NumType
}
class GenericNumber<NumType>{
  zeroValue:NumType; // tsconfig文件 strictPropertyInitialization为false 就不提示属性“zeroValue”没有初始化表达式,且未在构造函数中明确赋值
  add:(x:NumType, y:NumType) => NumType
}
let myGeneric = new GenericNumber<number>()
myGeneric.zeroValue = 0
myGeneric.add = function(x,y){
  return x+y
}
let myGeneric2 = new GenericNumber<string>()
myGeneric2.zeroValue = ''
myGeneric2.add = function(x,y){
  return x+y
}

泛型约束

// 用一个接口或者type来定义Lengthwise
function loggingIdentity<Type extends Lengthwise>{}
interface Lengthwise {
  length:number
}
function loggingIdentity<Type extends Lengthwise>(arg:Type):Type{
  console.log(arg.length) // 类型“Type”上不存在属性“length” 解决:规定某个类型
  return arg
}
// loggingIdentity(3)//类型“number”的参数不能赋给类型“Lengthwise”的参数。
loggingIdentity('hello')
loggingIdentity([1,2])

在泛型约束中使用类型参数

<Key extends keyof Type>
function getProperty<Type,Key extends keyof Type>(obj:Type, key:Key){
  return obj[key]
}

let x = {
  a:1,
  b:2,
  c:3,
  d:4
}
getProperty(x,'a')
// getProperty(x,'m') // 类型“"m"”的参数不能赋给类型“"a" | "b" | "c" | "d"”的参数

在泛型中使用类类型

// c是类类型,传入c时必须给c定义类型
function create<Type>(c: {new():Type}):Type{
  return new c()
}

function create<Type>(c: {new():Type}):Type{
  return new c()
}

class BeeKeeper{
  hasMask: boolean = true
}
class ZooKeeper{
  nametag:string = 'Mikle'
}
class Animal{
  numLegs:number = 4
}
class Bee extends Animal{
  keeper:BeeKeeper = new BeeKeeper()
}
class Lion extends Animal{
  keeper:ZooKeeper = new ZooKeeper()
}
function createInstance<A extends Animal>(c:new()=> A):A{
  return new c()
}
createInstance(Lion).keeper.nametag
createInstance(Bee).keeper.hasMask
// createInstance(BeeKeeper) // 类型“typeof BeeKeeper”的参数不能赋给类型“new () => Animal”的参数。 类型 "BeeKeeper" 中缺少属性 "numLegs",但类型 "Animal" 中需要该属性。

keyof类型操作符

接收一个对象类型产生key字符串或者是数字字面量的一个结合或者是一个联合类型

type Point = {x:number;y:number}
// 返回"x"|"y"
type P = keyof Point
const p1:P = 'x'
const p2:P = 'y'
/* type Point = {x:number;y:number}
// 返回"x"|"y"
type P = keyof Point
const p1:P = 'x'
const p2:P = 'y'
const p3:P = 'z' // 不能将类型“"z"”分配给类型“keyof Point” */

type Arrayish = {
  [n:number]:unknown
}
type A = keyof Arrayish
const a:A = 0

type Mapish = {
  [k:string]:boolean
}
type M = keyof Mapish
const m1:M = 0
const m2:M = '0'
// const m3:M = true // 不能将类型“boolean”分配给类型“string | number”。

typeof类型操作符

引用一个变量或者是属性的类型
typeof 只能修饰一个变量或者是某个对象里面的属性

let s = "hello"
let n:typeof s
n = 'world'
// console.log(typeof 'Hello World')

/* let s = "hello"
// 返回s的类型直接赋给了n
let n:typeof s
n = 'world'
n = 100 // 不能将类型“number”分配给类型“string” */

/* // ReturnType
type Predicate = (x:unknown) => boolean
type K = ReturnType

function f(){
  return {
    x:10,
    y:3
  }
}
// type P = ReturnType // “f”表示值,但在此处用作类型。是否指“类型 f”?
type P = ReturnType
const p:P = 100 // 不能将类型“number”分配给类型“{ x: number; y: number; }” */

/* // 不能在typeof后面调用这个函数试图去返回函数的返回结果的类型
// typeof 只能修饰一个变量或者是某个对象里面的属性
function msgbox(){}
// let shouldContinue:typeof msgbox('hello')
let shouldContinue:typeof msgbox
shouldContinue = 100 // 不能将类型“number”分配给类型“() => void” */

索引访问类型

去查询另外一个类型上的特定属性

type Person = {
  age:number
  name:string
  alive:boolean
}
type Age = Person["age"]
// Age为number类型
/* type Person = {
  age:number
  name:string
  alive:boolean
}
type Age = Person["age"]
let age: Age = '90' // 不能将类型“string”分配给类型“number” */

interface Person{
  name:string
  age:number
  alive:boolean
}
// type I1=string|number
type I1 = Person['age'|'name']
const i11:I1 = 100
const i12:I1 = ''
// const i13:I1 = true //不能将类型“boolean”分配给类型“I1”
// type I1=string|number|boolean
type I2 = Person[keyof Person]
const i21:I2 = 100
const i22:I2 = ''
const i23:I2 = true
// const i24:I2 = {}

type AliveOrName = 'alive'|'name'
type I3 = Person[AliveOrName]
const I31:I3 = true
const I32:I3 = 'hello'
// const I33:I3 = 100

// type I4 = Person['alive']

const MyArray = [
  {name:'Alice',age:15},
  {name:'Bob',age:23},
  {name:'Eve',age:38},
]
// type Person2 = {name:string,age:number}
type Person2 = typeof MyArray[number]
const p:Person2 = {
  name:'xx',
  age:12,
  // alive:true
}
type Age = typeof MyArray[number]['age']
const age:Age = 11
type Age2 = Person['age']
const age2:Age2 = 300

const key = 'age'
// type Age3 = Person[key] // “key”表示值,但在此处用作类型。是否指“类型 key”?
type Age3 = Person[typeof key]
type key1 = 'age'
type Age31 = Person[key1]

条件类型

SomeType extends OtherType? TrueType:FalseType

/* interface Animal{
  live():void
}
interface Dog extends Animal{
  woof():void
}
// type Example1 = number
type Example1 = Dog extends Animal ? number:string
// type Example2 = string
type Example2 = RegExp extends Animal ? number:string */

interface IdLabel{
  id:number
}
interface NameLabel{
  name:string
}
/* function createLabel(id:number):IdLabel
function createLabel(name:string):NameLabel
function createLabel(nameOrId:string | number):IdLabel | NameLabel
function createLabel(nameOrId:string | number):IdLabel | NameLabel {
  throw ''
} */

type NameOrId<T extends number | string> = T extends number ? IdLabel : NameLabel
function createLabel<T extends number | string>(idOrName:T):NameOrId<T>{
  throw ''
}
// type a1 = NameLabel
let a1 = createLabel('typeScript')
// type b1 = IdLabel
let b1 = createLabel(2.5)
// type c1 = NameLabel | IdLabel
let c1 = createLabel(Math.random() > 0.5 ? 'hello':42)

条件类型约束

type MessageOf = T extends {message:unknown}? T[‘message’] : never

// type MessageOf = T['message']
// type MessageOf = T['message']
/* type MessageOf = T extends {message:unknown} ? T['message']:never
interface Email{
  message:string
}
interface Dog{
  bark():void
}
// type EmailMessageContents = string
type EmailMessageContents = MessageOf
const emc: EmailMessageContents = 'balabala...'

type DogMessageContents = MessageOf
// const dmc: DogMessageContents = 'error' // 不能将类型“string”分配给类型“never”。
const dmc: DogMessageContents = 'error' as never */

type Flatten<T> = T extends any[] ? T[number]:T
// type Str = string
type Str = Flatten<string[]>
// type Num = number
type Num = Flatten<number>

在条件类型内推理

提供一种方法推断我们在真实分支中使用infer关键字来进行对比的类型

type Flatten<Type> = Type extends Array<infer Item> ? Item : Type
type GetReturnType<Type> = Type extends (...args:never[]) => infer R
  ? R
  : never
// type Num12 = number
type Num12 = GetReturnType<() => number>
let num: Num = 100
// type Str12 = string
type Str12 = GetReturnType<(x:string) => string>
let str:Str = ''
// type Bools = boolean[]
type Bools = GetReturnType<(a:boolean, b:boolean) => boolean[]>
let bools:Bools = [true,false]
// type Never = never
type Never = GetReturnType<string>
let nev: Never = 'error' as never

function stringOrNum(x:string):number
function stringOrNum(x:number):string
function stringOrNum(x:string | number):string | number
function stringOrNum(x:string | number):string | number{
  return Math.random() > 0.5 ? 'hello':23
}
// type T1 = string | number
type T1 = ReturnType <typeof stringOrNum>
const t1:T1 = 100
const t2:T1 = '1'
// const t3:T1 = true

分布式条件类型

type ToArray<Type> = Type extends any ? Type[]:never
type StrArrOrNumArr = ToArray<string | number>
type ToArray<Type> = Type extends any ? Type[] : never

/* // type StrArrOrNumArr = string[] | number[]
// type StrArrOrNumArr = (string | number)[]
type StrArrOrNumArr = ToArray
// let saon: StrArrOrNumArr = 100 // 不能将类型“number”分配给类型“StrArrOrNumArr”。
let saon: StrArrOrNumArr = [] */

type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never
type StrArrOrNumArr = ToArrayNonDist<string | number>
// let saon: StrArrOrNumArr = [true] // 不能将类型“boolean”分配给类型“string | number”。
let saon: StrArrOrNumArr = [1]

你可能感兴趣的:(typeScript,typescript,学习,javascript)