TypeScript入门 | 青训营笔记

67 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第4天。

JS没有表达不同代码单元之间关系的能力。编程最常见的是类型错误。TS的目标是成为JS程序的静态类型检查器,是JS的超集,添加了可选的对象类型。TS可以检查非异常故障。静态类型检查设置的越严格,越需要编译更多的代码,但从长远来看是值得的。可读性增强,可维护性增强。

动态语言和静态语言的差异主要是:编译发生在执行前还是执行时

优化编译

  • tsc --init #生成配置文件:解决ts和js中函数名重复的problems
  • tsc --watch 自动编译:将修改后的TS直接编译成JS
  • tsc -noEmitOnError hello.ts 表示在TS出现错误时并不进行编译成JS

数据类型

变量上会写类型注释,没有写时TS会推测一个变量的类型。

let myName: string = 'Felixlu' //myName是字符串,不能将别的类型赋值给它。即便不写: string,TS也会推断出myName类型

三个基元类型:string、number、Boolean

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

数组类型

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

any:不希望某个特定值导致类型检查错误。当一个值的类型是any时可以访问它的任何属性,将它赋值给任何值。

let obj:any={
  x:0
}
//any不会进行任何检查,以下的代码都不会报错,编译成js后运行的话会报错。
obj.foo()
obj()
obj.bar=100
obj='hello'
const n:number=obj

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

匿名函数,当一个函数出现在TS可以确定它会被如何调用的地方的时候,这个函数的参数会自动指定类型。

定义函数形参的对象的类型:

function printCoord(pt:{x:number;y:number}){
console.log('坐标的x值为'+pt.x)
console.log('坐标的y值为'+pt.y)
}
printCoord({
  x:3,y:7
})

联合类型union

let id:number|string  //表示id可以是number或string中的一种
function welcome(x:string[]|string){ //表示x可以是字符串,也可以是数组中包字符串
  if(Array.isArray(x)){
    console.log('hello'+x.join('and')) //如果要用类型不同的方法,需要写if区分传入的类型再进行处理
  }else{
    console.log('welcome lone traveler'+x)
  }
}
welcome('A') //可以传入字符串 
welcome(['a','b','c']) //可以传入数组中放字符串
//number[]是数值数组,string[]是字符串数组

类型别名

type Point = {
  x:number;
  y:number
}
function print(pt:Point){
  ...
}
print({
  x:100,
  y:200
})

接口

interface ITF{
  x:number
  y:number
}
function print2(pt:ITF){
​
}
print({
  x:100,
  y:200
})

类型别名和接口之间的不同:几乎所有可以使用interface定义的类型都可以使用type定义,但接口可以拓展。

通过interface向现有的类型添加字段。但不能往type定义的类型中添加字段,只能通过&符号拓展。

类型断言

//两种类型断言的语法
const myCanvas=document.getElementById('main_canvas') as HTMLCanvasElementconst myCanvas2=<HTMLCanvasElement>document.getElementById('main_canvas')

文字类型

function printText(s:string,alignment:'left'|'right'|'center'){
  ...
}
printText('hello','left')
//数字类型
function compare(a:string,b:string): -1 | 0 | 1{
  return a===b? 0:a<b?1:-1
}
​
interface Options{
  width:number
}
function configure(x:Options|'auto'){
  ...
}
configure({
  width:100
})
configure('auto')
//布尔类型 必须是对应的布尔值
let b1:true=true
let b2:false=false

null和undefined

let x:undefined=undefined
let y:null=nullfunction liveDangerously(x?:number | null){
  console.log(x!.toFixed())
}  //上面感叹号表示你明确知道x不可能是null或undefined类型。问号?表示可以没有x这个参数

枚举

//枚举
enum Direction{
  Up=5,
  Down,
  Left,
  Right
}
console.log(Direction.Up) //5
console.log(Direction.Down) //6

类型缩小

宽类型=>窄类型。将特殊的检查称为类型防护。将类型用typeof细化为比类型声明更具体的范围的行为称为类型缩小。

typeof类型守卫

typeof strs === "object" |"string" | "number" | "bigint" |"boolean" | "symbol" |"undefined" |"function"  
//typeof null === "object"

真值缩小

function printAll(strs:string|string[]|null){
  ////strs&& 用于确保strs不是null
  if(strs && typeof strs ==="object"){
    for(const s of strs){
      console.log(s);
    }
  }else if(typeof strs==="string"){
    console.log(strs);
  }else{
    //...
  }
}

等值缩小

function multiplyValue(container:Container,factor:number){
  if(container.value!=null){
    console.log(container.value)
    container.value*=factor
  }
}
multiplyValue({value:5},6) //30
multiplyValue({value:undefined},6) //不输出 被过滤掉了

in操作符缩小

type Fish={swim:()=>void}
type Bird={fly:()=>void}
type Human={ swim?:()=>void;fly?:()=>void}
​
function move(animal:Fish|Bird|Human){
  if("swim" in animal){//这里写in来筛选一些属性
    return (animal as Fish).swim()//这里写as来确定类型,类型断言
  }
  return (animal as Bird).fly()
}

instanceof操作符缩小

function logValue(x:Date | string){
   if(x instanceof Date){
    console.log(x.toUTCString())
    //toUTCString() 方法根据世界时将 Date 对象转换为字符串。
   }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:number
x=1
console.log(x)
​
//let x=string
x='goodbye'
console.log(x)

受歧视的unions

interface Circle{
  kind:'circle'
  radius:number
}
interface Square{
  kind:'square'
  sideLength:number
}
type Shape=Circle|Square
//用switch来选则不同的类型
function getArea(shape:Shape){
  //因为radius属性是可选择的,可能没有,所以我们在radius后面加一个!,告诉ts这个属性是一定存在的。
  // return Math.PI*shape.radius!**2
  switch(shape.kind){
    case 'circle':
    return Math.PI*shape.radius**2
​
    case 'square':
      return shape.sideLength**2
  }
}

never

never进行穷尽性检查。