从零到一学习TypeScript(二)类型 与 对象

703 阅读7分钟

函数 和 对象

  • 函数的参数和返回值类型

函数是javaScript中重要的组成部分, typescript为函数提供了参数类型和返回值类型, 允许我们指定在声明函数参数时, 可以在每个参数后面添加类型注解
    function adny(msg: string) {
      return msg + 'hello'
    }
    console.log(adny('erkelost')); // 'erkelosthello'
    adny(123) // 类型为number 的参数 不能赋值给 类型为string 的参数
我们也可以添加返回值的类型注解,这个注解在函数的参数列表的后面, 和声明变量注解一样 我们通常情况下不需要返回类型注解 因为typescript会根据返回值的类型自动推断类型注解
  function add(num1: number, num2: number): number {
    return num1 + num2
  }
  • 匿名函数的参数

通常情况下 我们在定义一个函数时 ,都会给函数的参数添加上类型注解
function send(msg: string, num: number){}
但是我们如果能根据上下文环境,推断出来这个参数的类型,那么我们可以不添加类型注解
const arr: string[] = ['adny', 'erkelost']
arr.forEach(item => console.log(item))
我们并没有指定item 的类型 但是item 是一个 string 类型 这是因为typescript 会根据foreach函数类型和 数组的string类型 推断出item的类型 这个过程称之为 上下文类型 可以帮助我们确定参数 和返回值类型
  • 对象类型

如果我们希望函数的参数是一个对象类型
我们可以使用一个对象类型 在对象中 我们可以添加属性告知 typescript 我们需要的是什么参数类型 属性之间可以用, : 分割 如果我们不指定类型 那么就是 any类型
function setInfo(obj: {name:string ; age: number}) {
  console.log(obj.name);
  console.log(obj.age);
}
function setInfo(obj: {name:string , age: number}) { // 可以用逗号 和 分号进行分割
  console.log(obj.name);
  console.log(obj.age);
}
function setInfo(obj: {name:string; age}) {
  console.log(obj.name);
  console.log(obj.age); // 这时的age就是any类型
}
  • 可选类型

对象属性也可以添加 指定 那些属性是可选的 用?:添加
function add (data: {a: number, b: number, c?: number}) {
  return data.a + data.b +  data.c  
}
console.log(add({a:10, b:20, c:30}));
因为可能属性c为可选类型 可能值为undefined 在typescript会报错
function add (data: {a: number, b: number, c?: number}) {
  
}
console.log(add({a:10, b:20, c:30}));
所以我们可以 判断c是否存在 值不为undefined的情况下
function add (data: {a: number, b: number, c?: number}) {
  if (data.c) {
    return data.a + data.b +  data.c  
  }
}
console.log(add({a:10, b:20, c:30}));
可选类型 可以看做是定义类型 和 undeifined 类型结合的联合类型 我们可以给参数传递undeifined
function say(msg?: string) {
  console.log(msg);
}
say('hello')
say(undefined)
say(null) // 报错 类型只有 string 和 undefined
  • 联合类型

typescript中允许我们使用运算符在构建新类型 联合类型 就是 由两个 或者多个类型组成的新类型 表示类型可以是其中的任何一个值
function getInfo(name: number | string) {
  console.log(name);
}
getInfo('123')
getInfo(123)
getInfo(false) // 类型“boolean”的参数不能赋给类型“string | number”的参数
那么我们如何使用联合类型呢 我们可以使用缩小联合类型 来处理 缩小代码结构 推断出更加准确的类型
function getInfo(name: number | string | null) {
  if (typeof name === 'string') {
    console.log(name.toUpperCase())
  } else if (typeof name === 'number') {
    console.log(name.toFixed(2));
  } else {
    console.log(name);
  }
}
getInfo(123.99999) // 123.00
getInfo('adb') // ABD
getInfo(null) // null
使用typeof 来帮助我们确定类型
  • 类型别名

在我们给对象类型和联合类型命名时, 如果我们要在多个地方使用 就要编写多次 所以我们可以给类型起一个别名
type nameType = number | string | null
function getInfo(name: nameType) {
  if (typeof name === 'string') {
    console.log(name.toUpperCase())
  } else if (typeof name === 'number') {
    console.log(name.toFixed(2));
  } else {
    console.log(name);
  }
}
getInfo(123.99999)
getInfo('adb')
getInfo(null)
type addType = {
  a: number,
  b: number,
  c?: number
}
function add (data: addType) {
  if (data.c) {
    return data.a + data.b +  data.c  
  }
}
console.log(add({a:10, b:20, c:30}));
  • 类型断言 as

类型断言(Type Assertion)可以用来手动指定一个值的类型。

有时候typescript 无法准确地获得一个值的类型 我们可以手动来获取 就需要使用类型断言

const img = document.getElementById('#app')  // 类型为 HTMLElement
img.src = 'https://wallhaven.cc/' // 这时候回报错 类型“HTMLElement”上不存在属性“src”。

为了 让我们img 能够得到具体的类型 来获得相应的属性 我们可以使用类型断言

const img = document.getElementById('#app') as HTMLImageElement
img.src = 'https://wallhaven.cc/'

typescript 只允许类型断言转换为 更具体 或者不具体的类型版本 来防止强制转换

const name: string = 'erkelost' as number
// 类型 "string" 到类型 "number" 的转换可能是错误的,因为两种类型不能充分重叠。如果这是有意的,请先将表达式转换为 "unknown"。

上一篇我们可以知道 任何类型都可以是unknown类型 但是 unknown类型只能被赋值给any 类型 和 unknown 类型
const name = ('erkelost' as unknown) as number
这样我们 是不能在前面给name 声明类型 否则会报错
  • 非空类型断言 --------- !

在我们前面讲到的 可选类型 也就是 类型 和 undefined 结合的联合类型
function getName(name?: string) {
  // error TS2532: Object is possibly 'undefined'.
  console.log(name.toUpperCase());
}
getName('erke')
但是如果我们确定我们传入的参数是有效的 那么我们就可以使用非空类型断言
function getName(name?: string) {
  // error TS2532: Object is possibly 'undefined'.
  console.log(name!.toUpperCase());
}
getName('erke')
非空类型断言使用的是! 表示我们确定某个标识符是有值的 这样我们就可以跳过typescript对我们的监测
  • 可选链

如果当对象属性不存在时 就会短路 报错 undefined 我们在定义类型的时候 只要type里面的 都要和 我们的对象 属性一一对应 可选链属性 是 ES11 中添加的属性 Typescript 中可以使用

我们可以使用?. 的操作符 他的作用是当对象的属性不存在是 就会短路 返回 undefined

type objType = {
  name: string,
  age: number,
  obj: {
    name: string,
    age: number
  }
}
const info: objType = {
  name: 'erkelost',
  age: 9999999
}  
 // 已声明“info”,但从未读取其值。ts(6133)

// 类型 "{ name: string; age: number; }" 中缺少属性 "obj",但类型 "objType" 中需要该属性。ts(2741)

// [05_联合类型.ts(64, 3): ]()在此处声明了 "obj"

type objType = {
  name: string,
  age: number,
  obj?: {
    name: string,
    age: number
  }
}
const info: objType = {
  name: 'erkelost',
  age: 9999999
}
console.log(info.name);
console.log(info.obj?.name); // undefined
  • ?? 和 !! ES11 新特性

!! 是 把一个类型转换成boolean类型
?? 是 逻辑操作符当操作符的左侧是null 或者 undefined 的时候 返回右侧操作数 否则返回左侧的
const a: string = 'erkelsot'
console.log(!!a); // true
const erke = undefined
console.log(erke ?? 'adny'); // adny
  • 字面量类型

除了我们上述类型 之外 还有 一个字面量类型 字面意思 就是 把我们赋值 当成 类型
const message: "Hello World" = "Hello World"  // const message: "Hello World" 字面量类型
我们可以默认在函数中 将多个类型联合在一起

type styleType = 'bottom' | 'padding' | 'align'

function changeStyle(style: styleType) {
  console.log(`样式${style}`);
}
changeStyle('bottom') // 样式bottom
changeStyle('padding') // 样式padding
changeStyle('align') // 样式align 
changeStyle('border') // 类型“"border"”的参数不能赋给类型“styleType”的参数
  • 字面量推理

const options = {
  url: "https://wallhaven.cc/",
  method: "POST"
}

type Methods = 'GET' | 'POST'
function request(url?: string, method?: Methods) {
  console.log(url, method);
}
request(options.url, options.method) // options.method 会报错 类型“string”的参数不能赋给类型“Methods”的参数。
我们只能传递Methods类型中的字符串类型 而不能随便传递一个随意的字符串 这样会有安全隐患
因为我们的options.method 是string 类型 我们函数 参数类型是 Methods 类型 如果我们options.method是一个 字符串123 那么 我们本来只能传递get 和 post 字符串 这样就会报错
1. 第一种方法 我们可以限定options 的类型 这样就能防止 传递别的字符串
type Request = {
  url: string,
  method: Methods
}  //  限定options传递的类型
const options: Request = {
  url: "https://wallhaven.cc/",
  method: "POST"
}


type Methods = 'GET' | 'POST'
function request(url?: string, method?: Methods) {
  console.log(url, method);
}
request(options.url, options.method)
2. 第二种方法 如果我们能够确定我们的对象属性字符串确实 传递正确后 我们可以 使用类型断言 把一个宽泛的类型转换成一个具体的类型 来使用 消除安全隐患 主要是 我们的字符串 只能填 get 和 post 具有安全隐患
const options = {
  url: "https://wallhaven.cc/",
  method: "POST"
}


type Methods = 'GET' | 'POST'
function request(url?: string, method?: Methods) {
  console.log(url, method);
}
request(options.url, options.method as Methods) // 做一个类型断言
  • 3. 第三种方法 字面量推理 在给我们传递的对象后面 添加 一个 as const 变成一个只读对象 就可以确定我们传递的参数如果 就是一个 get 或者 post 他就能确定 我们使用的 字符串 就是这两个
const options = {
  url: "https://wallhaven.cc/",
  method: "POST"
} as const
const options: {
  readonly url: "https://wallhaven.cc/";
  readonly method: "POST";
}
这样我们拿到的这个对象是一个只读类型 就可以确定 他的值 只能是 post 不可以修改 把一个比较宽泛的类型 转换成了一个具体的一个值 字面量的类型