ts的基础(2)

(十一)元祖

越界后,会推断为一个联合类型,比如[number,string] , 越界后为 number|string 类型。

可以只读,readonly

可选,可以命名,用?表示可选。

let arr:[number,boolean] = [1,false]

arr[0] = 345
// arr[0] = '234' // 错误

arr.push(234)
// arr.push('234')  // 错误

arr = [34,true]

let arr1: readonly [number,boolean] = [1,false]
// arr1[0] = 4567  // 错误
arr1 = [65,false]  // 可以
// arr1.push(234) // 错误


let arr2: [x:number,y?:boolean] = [4]

// 读到元祖的类型
type first = typeof arr[0]  // number
type two = typeof arr['length']  //2

(十二)枚举 enum

默认从0开始,对象的方式输出

指定初始值,比如1,后续会递增。

也可以自定义指定具体的值  如:red = 1  green = 3

也可以使用字符串自定义  如:red = ‘red’   green = 'green'

也可以穿插使用 如: red = 2  green = ‘gn’

const关键字创建enum的话,与常规枚举的区别在于,在编译时会将其内联,而不会在运行时创建对象。这意味着无法在运行时使用const枚举的属性名称来访问枚举的值。不能反向映射。

// 默认从0开始的,对象的方式输出
enum Color{
  red,
  green,
  blue
}

// 增长枚举,后续会递增
enum Color1{
  red = 1,
  green,
  blue
}

// 自定义枚举,使用=
enum Color2{
  red = 1,
  green = 5,
  blue = 6
}

// 字符串枚举
enum Color3{
  red = 'red',
  green = 'green',
  blue = 'blue'
}

// 穿插使用,也支持
enum Isok{
  yes = 1,
  no = 'no'
}

const enum Types{
  fail,
  success
}
let code = 0
if(code == Types.fail){}
// 这里编译,会将 Types.fail 直接编译为常量(这里为0),而不加const,会编译为一个对象


// 反向映射,可以key读value值,也可以value读key值
enum Types2{
  fail,
  success
}
let success:number = Types2.success
let key = Types2[success]
console.log(`value:${success}, key:${key}`);

(十三)type 类型推论,类型别名

类型推论:ts自己进行变量的推论类型,如果没有赋值,默认为any

可以给类型起别名,关键字type

type和interface的区别:1,interface可以extends继承  2,interface必须是属性定义,不能省略直接定义  3, interface遇到重名的,会自己合并,type不可以

!!!extends在type中是包含的意思。

type num = 1 extends number ? 1 : 0   // 1    左边作为右边类型的子类型。

type s = string
type s1 = string & number
type sn = string | number
type fn = ()=>void
type ar = []
type ob = {name:string}

let str:s = '信息'

// extends 在type中是包含的意思,
// 左边的值会作为右边类型的子类型。
// 记住类型的顶级顺序,never在最底层,不包含1.
type num = 1 extends number ? 1 : 0   // 1
type num1 = 1 extends any ? 1 : 0   // 1
type num2 = 1 extends unknown ? 1 : 0   // 1
type num3 = 1 extends Object ? 1 : 0   // 1
type num4 = 1 extends Number ? 1 : 0   // 1
type num5 = 1 extends never ? 1 : 0   // 0

(十四)never

通常表示不存在状态,无法达到预期

type A = string & number // never

可以作为兜底逻辑,无法被赋值为某个类型,所以会出现提示,避免出错

type A = string & number  // never

// 一执行就报错,所以never更合理
function xx(): never{
  throw new Error('执行错误')
}

// 写在联合类型中,never会直接被忽略
type B = string | number | never

type K = '唱' | '跳' | 'rap'

function kun(value:K){
  switch(value){
    case '唱':
      break
    case '跳':
      break
    case 'rap':
      break
    default:
      // 兜底逻辑 (无法被赋值string,会出现提示,所以避免错误发生)
      const error:never = value
      break
  }
}

(十五)Symbol

Symbol 是个全局的方法。参数 string|number ,空值

表示唯一性,即使完全一样,返回的值也是唯一性的(内存地址不同)

那什么时候来个symbol可以返回true呢,使用Symbol.for(xxx) 来判断。

!!!在对象种使用Symbol的话,如何读到symbol,

for in 读不到symbol, Object.keys() 读不到symbol,Object.getOwnPropertyNames() 读不到symbol,Object.getOwnPropertySymbols() 只能读到只读的symbol,无法读到其他。

那如何基础类型和symbol都能读到呢, Reflect,映射。 Reflect.ownKeys().

let a1:symbol = Symbol(1)
let a2:symbol = Symbol(1)

console.log(a1,a2,a1==a2);   // Symbol(1) Symbol(1) false

// for 和 Symbol有区别,for会在全局中查询是否注册过key值,如果有直接拿来用,如果没有就创建一个
console.log(Symbol.for('xiao') === Symbol.for('xiao'));  // true

// 可以确保key值的唯一性
let obj = {
  name:'11',
  [a1]:'33',
  [a2]:'45'
}

// for in 的问题:这里只能读取到name,无法读到symbol
for(let key in obj){
  console.log(key);
}
// Object.keys() 也读取不到symbol
console.log(Object.keys(obj));

// Object.getOwnPropertyNames() 也读取不到symbol
console.log(Object.getOwnPropertyNames(obj));

// Object.getOwnPropertySymbols() 可以读取不到symbol,但只能读到到symbol,不包含其他
console.log(Object.getOwnPropertySymbols(obj));


// 如何symbol和基础类型都取值到,使用Reflect,映射
console.log(Reflect.ownKeys(obj));

(十六)生成器,迭代器

生成器写法: function *A(){ yield xxx }   

按照顺序,next返回都执行一次,done:true 表示没有东西可以迭代了,判断是否输出完毕了。

迭代器 set ,map

set天然去重,数字,字符串,不能是对象。

map 键值对形式,key可以是引用类型。 

迭代器作用:可以遍历伪数组,遍历可迭代的。Symbol.iterator

for of是迭代器的语法糖, for of是不能迭代对象的,没有iterator,解构的原理也是iterator。

// 生成器
function* gen() {
  yield '小心'  // 同步异步
  yield Promise.resolve('花卉')
  yield '中啦'
  yield '刀哥'
}

// 按照顺序的,每次next返回都执行一次
// done:true 表示没有东西可以迭代了。
// 可以通过done的值来判断 生成器是否输出完毕了。
const man = gen()  // Generator
console.log(man.next()); // { value: '小心', done: false }
console.log(man.next()); // { value: Promise { '花卉' }, done: false }
console.log(man.next());
console.log(man.next());
console.log(man.next()); // { value: undefined, done: true }

// 迭代器
// set map
let set: Set<number> = new Set([1, 1, 1, 2, 2, 3, 4]) // 天然去重的, 只能是数字,字符串,对象不支持

console.log(set); // Set(4) { 1, 2, 3, 4 }

let map: Map<any, any> = new Map()

let arr = [1, 2, 3]
map.set(arr, 'ddd')  // map的key值可以使用引用类型。
console.log(map.get(arr));

function args() {
  console.log(arguments); // 伪数组
}
// let list = document.querySelectorAll('div')  // 伪数组,不过可以使用foreach
// 有什么方法可以同时遍历以上类型的呢  -- 迭代器 -- Symbol.iterator
const each = (value: any) => {
  let It: any = value[Symbol.iterator]()
  let next: any = { done: false }
  while (!next.done) {
    next = It.next()
    if (!next.done) {
      console.log(next.value);
    }
  }
}

// each(map)  // [ [ 1, 2, 3 ], 'ddd' ]  key和value的数组

// 迭代器的语法糖  for of
// 注意:for of 对象是不能用的,没有iterator
// 解构的底层原理,也是调用 iterator 的
for (let value of map) {
  console.log(value);
}


// 对象 支持 for of 写法
let obj = {
  max: 4,
  current: 0,
  [Symbol.iterator]() {
    return {
      max: this.max,
      current: this.current,
      next() {
        if (this.current === this.max) {
          return {
            value: undefined,
            done: true
          }
        } else {
          return {
            value: this.current++,
            done: false
          }
        }
      }
    }
  }
}

for (let value of obj) {
  console.log(value);
}

// 解构 - null 和 undefined 不能被解构
let x = [...obj]
console.log(x); //[ 0, 1, 2, 3 ]
let y = {...obj}
console.log(y);
// {
//   max: 4,
//   current: 0,
//   [Symbol(Symbol.iterator)]: [Function: [Symbol.iterator]]
// }

(十七) 泛型

动态类型,避免因为类型不同出现的重复性工作,将类型变成动态的。

写法: 自定义命名,在名字后面,<>里定义,如:function da<T>(a:T,b:T): Array<T>{}

泛型可以是多个,也可以使用默认值。arr<T=number,K=number>(a:T,b:K){}

// 写法  自定义命名,在名字后面,<>里自定义
function da<T>(a:T,b:T):Array<T>{
  return [a,b]
}

da(1,2)
da('1','3')
da(false,true)

type A<T> = string | number | T

let a:A<boolean> = false
let b:A<undefined> = undefined
let c:A<null> = null

interface Data<T>{
  msg:T
}

let data:Data<string> = {
  msg:'dddddddddd'
}

// 泛型也可以支持多个, 也可以使用默认值
function arr<T=number,K=number>(a:T,b:K):Array<T | K>{
  return [a,b]
}

const axios = {
  get<T>(url:string):Promise<T>{
    return new Promise((resolve,reject)=>{
      let xhr: XMLHttpRequest = new XMLHttpRequest()  // node环境不行,编译为js
      xhr.open('GET',url)
      xhr.onreadystatechange = ()=>{
        if(xhr.readyState == 4 && xhr.status == 200){
          resolve(JSON.parse(xhr.responseText))
        }
      }
      xhr.send(null)
    })
  }
}

interface Res{
  message:string
  code:number
}

axios.get<Res>('./json/data.json').then(res=>{
  console.log(res);
})

(十八)泛型约束

<T extends number> 约束泛型的范围   在extends后面跟一个约束类型。

keyof 会将类型视为联合类型。

let obj = {
  name:'小',
  sex:'女'
} 
type key = keyof typeof obj  // "name" | "sex"

约束取值,这样更加安全。

for(let key in obj)   相当于循环操作。

function add<T extends number>(a:T,b:T){
  return a+b
}

add(1,3)

interface Len{
  length:number
}

function fn<T extends Len>(a:T){
  console.log(a.length);
}

function ob<T extends object,K extends keyof T>(obj:T,key:K){
  return obj[key]  // 约束取值,认为安全
}

ob(obj,'name')
ob(obj,'sex')

// 使用工具实现属性可选
interface Pat{
  name:string
  age:number
  sex:string
}

// for in for(let key in obj)   相当于循环操作
type Opt<T extends object> = {
  [key in keyof T]?: T[key]
}
type Opt2<T extends object> = {
  readonly [key in keyof T]: T[key]
}

type B = Opt<Pat>
type C = Opt2<Pat>

(十九)namespace  命名空间

ts和es2015一样,包含顶级import或者export的文件才会视为一个模块。

相反的,如果没有顶级的import或者export就会被视为全局可见了,那变量命名就容易出现冲突。

所以出现命名空间,相互独立。

namespace里,需要export才能被外界访问。 可以嵌套,可以抽离为一个文件,引入。可以简化,别名。如果重名,那么属性会合并。

namespace A{
  export const a = 1  // 命名空间,需要export才能在外界访问
}

// 可以嵌套
namespace B{
  export namespace inner{
    export const a = 3
  }
}

console.log(A.a);
console.log(B.inner.a);

// 可以抽离为一个文件,引入
import { IN2 } from "./index2";
console.log(IN2.ab);

// 简化,别名
import BB = B.inner
console.log(BB.a);

// 如果重名,那么属性会合并

(旧版的ts-node不识别,需要编译之后查看,新版的ts-node可以识别了。)

(二十)三斜线指令

///<reference path='xxxx'/>  声明文件的依赖,只能在文件的最顶端,单行注释。

引入后,可以使用里面的变量,类似于export,import。

还可以指定声明文件:/// <reference types='node'/>

/// <reference path='./index2.ts'/>
/// <reference path='./index3.ts'/>
/// <reference types='node'/>

/*
  三斜线指令
  /// <reference path="..."/>  声明文件间的依赖
  只能在文件最顶端,单行注释。

  引入后,可以使用里面的变量。类似于export,import

  还可以指定声明文件:/// <reference types='node'/>

*/

namespace A{
  export const a = 45
}

console.log(B.b);
console.log(A.c);
console.log(A.a);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值