Typescript

本文详细介绍了TypeScript的基础知识,包括变量声明、数据类型(如字符串、数字、布尔、空值、Any和Unknown)、对象类型(如对象、接口、数组、函数)、类型断言以及枚举和元组的使用。文章还讨论了类型系统中的安全性和最佳实践。
摘要由CSDN通过智能技术生成

安装

npm i ts-node --g

npm i @types/node --save-dev

编译 ts 文件:tsc index.ts

运行编译后的 js 文件

基础类型

字符串类型

字符串是使用string定义的

let a: string = '123'
//普通声明
 
//也可以使用es6的字符串模板
let str: string = `dddd${a}`

数字类型

let notANumber: number = NaN;//Nan
let num: number = 123;//普通数字
let infinityNumber: number = Infinity;//无穷大
let decimal: number = 6;//十进制
let hex: number = 0xf00d;//十六进制
let binary: number = 0b1010;//二进制
let octal: number = 0o744;//八进制s

布尔类型

let booleand: boolean = true //可以直接使用布尔值
 
let booleand2: boolean = Boolean(1) //可以通过函数返回布尔值

注意,使用构造函数 Boolean 创造的对象不是布尔值:

let createdBoolean: boolean = new Boolean(1)
//这样会报错 应为事实上 new Boolean() 返回的是一个 Boolean 对象 
let createdBoolean: Boolean = new Boolean(1)

空值类型

function voidFn(): void {
    console.log('test void')
}

void也可以定义undefined 和 null类型

let u: void = undefined
let n: void = null;        //非严格模式下

Null和undefined

let u: undefined = undefined;//定义undefined
let n: null = null;//定义null

void 和 undefined 和 null 最大的区别

与 void 的区别是,undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 string 类型的变量:

//这样是没问题的
let test: null = null
let num2: string = "1"
 
num2 = test
 
//或者这样的
let test: undefined = undefined
let num2: string = "1"
 
num2 = test

//这样写会报错 void类型不可以分给其他类型
let test: void = undefined
let num2: string = "1"
 
num2 = test

Any 和 unknown

Any 类型 和 unknown 是顶级类型,随时切换类型都可以 可以对 any 进行任何操作,不需要检查类型,使用any 就失去了TS类型检测的作用

let anys:any = 123
anys = '123'
anys = true

TypeScript 3.0中引入的 unknown 类型也被认为是 top type ,但它更安全。与 any 一样,所有类型都可以分配给unknown

unknow类型比any更加严格

//unknown 可以定义任何类型的值
let value: unknown;
 
value = true;             // OK
value = 42;               // OK
value = "Hello World";    // OK
value = [];               // OK
value = {};               // OK
value = null;             // OK
value = undefined;        // OK
value = Symbol("type");   // OK
 
//这样写会报错unknow类型不能作为子类型只能作为父类型 any可以作为父类型和子类型
//unknown类型不能赋值给其他类型
let names:unknown = '123'
let names2:string = names
 
//这样就没问题 any类型是可以的
let names:any = '123'
let names2:string = names   
 
//unknown可赋值对象只有unknown 和 any
let bbb:unknown = '123'
let aaa:any= '456'
 
aaa = bbb

区别2

如果是any类型在对象没有这个属性的时候还在获取是不会报错的
let obj:any = {b:1}
obj.a
 
 
如果是unknow 是不能调用属性和方法
let obj:unknown = {b:1,ccc:():number=>213}
obj.b
obj.ccc()

unknown 更安全

object、Object、{}

  • Object

类型是跟原型链有关的原型链顶层就是Object,所以值类型引用类型最终都指向Object,所以他包含所有类型。

let a: Object = 123;
let b: Object = {};
let c: Object = () => 123;
  • object

object 代表所有非值类型的类型(引用类型),例如 数组 对象 函数等,常用于泛型约束

let o:object = {}//正确
let o1:object = []//正确
let o2:object = ()=>123 //正确
let b:object = '123' //错误
let c:object = 123 //错误
  • {}字面量类型

理解成new Object 就和我们的第一个Object基本一样 包含所有类型

let a1: {} = {name:1} //正确
let a2: {} =  () => 123//正确
let a3: {} = 123//正确
// 赋值之后是没办法修改的
// 看看下面的这个错误案例
a1.age = 18

接口和对象类型

定义对象的方式要用关键字interface(接口),使用interface来定义一种约束,让数据的结构满足约束的格式。

//使用接口约束的时候不能多一个属性也不能少一个属性,必须与接口保持一致
interface Person {
    b:string,
    a:string
}
 
const person:Person  = {
    a:"213"
}
//重名interface  可以合并
interface A{name:string}
interface A{age:number}
var x:A={name:'xx',age:20}

//继承
interface A{
    name:string
}
 
interface B extends A{
    age:number
}
 
let obj:B = {
    age:18,
    name:"string"
}

任意属性 [propName: string]

需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集

// 后端返回的值中,我只需要 name 和 age 属性,其他的属性我不想定义时,可以使用 任意属性
interface Person {
  name: string;
  age: number;
  [propName: string]: any;
}

const person: Person = {
  name: "lisi",
  age: 18,
  height: 180,
  id: "123",
};

可选属性 ?

//可选属性的含义是该属性可以不存在

interface Person {
    b?:string,
    a:string
}
 
const person:Person  = {
    a:"213"
}

只读属性 readonly

readonly 只读属性是不允许被赋值的只能读取

//这样写是会报错的
//应为a是只读的不允许重新赋值
interface Person {
    readonly a: string,
    [propName: string]: any;
}
 
const person: Person = {
    a: "213",
    c: "123"
}
 
person.a = 123

只读属性用途:1. 后端返回的 id 可以设置为只读属性 2. 封装的组件,组件中的方法不希望被别人修改,只能被调用。

添加函数

interface Person {
    b?: string,
    readonly a: string,
    [propName: string]: any;
    cb:()=>void
}
 
const person: Person = {
    a: "213",
    c: "123",
    cb:()=>{
        console.log(123)
    }
}

数组类型

基本类型
1. 类型[ ] 
//类型加中括号
let arr:number[] = [123]
//这样会报错定义了数字类型出现字符串是不允许的
let arr:number[] = [1,2,3,'1']
//操作方法添加也是不允许的
let arr:number[] = [1,2,3,]
arr.unshift('1')
 
 
var arr: number[] = [1, 2, 3]; //数字类型的数组
var arr2: string[] = ["1", "2"]; //字符串类型的数组
var arr3: any[] = [1, "2", true]; //任意类型的数组

2. Array<类型>
let arr:Array<number> = [1,2,3,4,5]


对象数组
interface people {
  name: string;
  age: number;
}

let arr: people[] = [
  {
    name: "lisi",
    age: 18,
  },
  {
    name: "zhangsan",
    age: 20,
  },
];

二维数组
let arr: number[][] = [
  [1, 2, 3],
  [4, 5, 6],
];

用接口表示数组

interface NumberArray {
    [index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
//表示:只要索引的类型是数字时,那么值的类型必须是数字。

arguments类数组

// 错误
function Arr(...args:any): void {
    console.log(arguments)
    //错误的arguments 是类数组不能这样定义,arguments是类数组,没有数组的那些方法
    let arr:number[] = arguments
}
Arr(111, 222, 333)
 // 正确
function Arr(...args:any): void {
    console.log(arguments) 
    //ts内置对象IArguments 定义
    let arr:IArguments = arguments
}
Arr(111, 222, 333)
 
//其中 IArguments 是 TypeScript 中定义好了的类型,它实际上就是:
interface IArguments {
    [index: number]: any;
    length: number;
    callee: Function;
}

函数扩展

//注意,参数不能多传,也不能少传 必须按照约定的类型来
const fn = (name: string, age:number): string => {
    return name + age
}
fn('张三',18)
  • 函数的可选参数?

//通过?表示该参数为可选参数
const fn = (name: string, age?:number): string => {
    return name + age
}
fn('张三')
  • 函数参数的默认值

const fn = (name: string = "我是默认值"): string => {
    return name
}
fn()
  • 接口定义函数

//定义参数 num 和 num2  :后面定义返回值的类型
interface Add {
    (num:  number, num2: number): number
}
 
const fn: Add = (num: number, num2: number): number => {
    return num + num2
}
fn(5, 5)
 
 
interface User{
    name: string;
    age: number;
}
function getUserInfo(user: User): User {
  return user
}
  • 函数重载

重载函数执行操作函数 两部分组成

重载是方法名字相同,而参数不同,返回类型可以相同也可以不同。

如果参数类型不同,则参数类型应设置为 any

参数数量不同你可以将不同的参数设置为可选。

// 重载函数
function fn(params: number): void
function fn(params: string, params2: number): void

// 执行操作函数
function fn(params: any, params2?: any): void {
    console.log(params)
    console.log(params2)
}

fn(123)        // 123,undefined
fn('123',456)  // ‘123’,456

类型断言 | 联合类型 | 交叉类型

联合类型

//例如我们的手机号通常是13XXXXXXX 为数字类型 这时候产品说需要支持座机
//所以我们就可以使用联合类型支持座机字符串
let myPhone: number | string  = '010-820'
 
 
//这样写是会报错的应为我们的联合类型只有数字和字符串并没有布尔值
let myPhone: number | string  = true

函数使用联合类型

// 例如后端有时候会用 0 和 1区分,有时候以 true 和 false 区分
const fn = (something:number | boolean):boolean => {
     return !!something
}

交叉类型

类似 extend,多种类型的集合,联合对象将具有所联合类型的所有成员

interface People {
  age: number,
  height: number
}
interface Man{
  sex: string
}
const xiaoman = (man: People & Man) => {
  console.log(man.age)
  console.log(man.height)
  console.log(man.sex)
}
xiaoman({age: 18,height: 180,sex: 'male'});

类型断言

错误写法:这样写是有警告的应为B的接口上面是没有定义run这个属性的
interface A {
       run: string
}
 
interface B {
       build: string
}
 
const fn = (type: A | B): string => {
       return type.run
}

正确写法:可以使用类型断言来推断他传入的是A接口的值
interface A {
       run: string
}
 
interface B {
       build: string
}
 
const fn = (type: A | B): string => {
       return (type as A).run
}
或者
const fn = (type: A | B): string => {
  return (<A>type).run;
};

需要注意的是,类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误

内置对象

ECMAScript 的内置对象

// Boolean、Number、string、RegExp、Date、Error
let b: Boolean = new Boolean(1)
console.log(b)
let n: Number = new Number(true)
console.log(n)
let s: String = new String('哔哩哔哩关注小满zs')
console.log(s)
let d: Date = new Date()
console.log(d)
let r: RegExp = /^1/
console.log(r)
let e: Error = new Error("error!")
console.log(e)

DOM 和 BOM 的内置对象

Document、HTMLElement、Event、NodeList 等
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
//读取div 这种需要类型断言 或者加个判断应为读不到返回null
let div:HTMLElement = document.querySelector('div') as HTMLDivElement
document.addEventListener('click', function (e: MouseEvent) {
    
});
//dom元素的映射表
interface HTMLElementTagNameMap {
    "a": HTMLAnchorElement;
    "abbr": HTMLElement;
    "address": HTMLElement;
    "applet": HTMLAppletElement;
    "area": HTMLAreaElement;
    "article": HTMLElement;
    "aside": HTMLElement;
    "audio": HTMLAudioElement;
    "b": HTMLElement;
    "base": HTMLBaseElement;
    "bdi": HTMLElement;
    "bdo": HTMLElement;
    "blockquote": HTMLQuoteElement;
    "body": HTMLBodyElement;
    "br": HTMLBRElement;
    "button": HTMLButtonElement;
    "canvas": HTMLCanvasElement;
    "caption": HTMLTableCaptionElement;
    "cite": HTMLElement;
    "code": HTMLElement;
    "col": HTMLTableColElement;
    "colgroup": HTMLTableColElement;
    "data": HTMLDataElement;
    "datalist": HTMLDataListElement;
    "dd": HTMLElement;
    "del": HTMLModElement;
    "details": HTMLDetailsElement;
    "dfn": HTMLElement;
    "dialog": HTMLDialogElement;
    "dir": HTMLDirectoryElement;
    "div": HTMLDivElement;
    "dl": HTMLDListElement;
    "dt": HTMLElement;
    "em": HTMLElement;
    "embed": HTMLEmbedElement;
    "fieldset": HTMLFieldSetElement;
    "figcaption": HTMLElement;
    "figure": HTMLElement;
    "font": HTMLFontElement;
    "footer": HTMLElement;
    "form": HTMLFormElement;
    "frame": HTMLFrameElement;
    "frameset": HTMLFrameSetElement;
    "h1": HTMLHeadingElement;
    "h2": HTMLHeadingElement;
    "h3": HTMLHeadingElement;
    "h4": HTMLHeadingElement;
    "h5": HTMLHeadingElement;
    "h6": HTMLHeadingElement;
    "head": HTMLHeadElement;
    "header": HTMLElement;
    "hgroup": HTMLElement;
    "hr": HTMLHRElement;
    "html": HTMLHtmlElement;
    "i": HTMLElement;
    "iframe": HTMLIFrameElement;
    "img": HTMLImageElement;
    "input": HTMLInputElement;
    "ins": HTMLModElement;
    "kbd": HTMLElement;
    "label": HTMLLabelElement;
    "legend": HTMLLegendElement;
    "li": HTMLLIElement;
    "link": HTMLLinkElement;
    "main": HTMLElement;
    "map": HTMLMapElement;
    "mark": HTMLElement;
    "marquee": HTMLMarqueeElement;
    "menu": HTMLMenuElement;
    "meta": HTMLMetaElement;
    "meter": HTMLMeterElement;
    "nav": HTMLElement;
    "noscript": HTMLElement;
    "object": HTMLObjectElement;
    "ol": HTMLOListElement;
    "optgroup": HTMLOptGroupElement;
    "option": HTMLOptionElement;
    "output": HTMLOutputElement;
    "p": HTMLParagraphElement;
    "param": HTMLParamElement;
    "picture": HTMLPictureElement;
    "pre": HTMLPreElement;
    "progress": HTMLProgressElement;
    "q": HTMLQuoteElement;
    "rp": HTMLElement;
    "rt": HTMLElement;
    "ruby": HTMLElement;
    "s": HTMLElement;
    "samp": HTMLElement;
    "script": HTMLScriptElement;
    "section": HTMLElement;
    "select": HTMLSelectElement;
    "slot": HTMLSlotElement;
    "small": HTMLElement;
    "source": HTMLSourceElement;
    "span": HTMLSpanElement;
    "strong": HTMLElement;
    "style": HTMLStyleElement;
    "sub": HTMLElement;
    "summary": HTMLElement;
    "sup": HTMLElement;
    "table": HTMLTableElement;
    "tbody": HTMLTableSectionElement;
    "td": HTMLTableDataCellElement;
    "template": HTMLTemplateElement;
    "textarea": HTMLTextAreaElement;
    "tfoot": HTMLTableSectionElement;
    "th": HTMLTableHeaderCellElement;
    "thead": HTMLTableSectionElement;
    "time": HTMLTimeElement;
    "title": HTMLTitleElement;
    "tr": HTMLTableRowElement;
    "track": HTMLTrackElement;
    "u": HTMLElement;
    "ul": HTMLUListElement;
    "var": HTMLElement;
    "video": HTMLVideoElement;
    "wbr": HTMLElement;
}

Promise

如果我们不指定返回的类型TS是推断不出来返回的是什么类型

指定返回的类型

函数定义返回promise 语法规则:Promise<T> 类型

function promise():Promise<number>{
   return new Promise<number>((resolve,reject)=>{
       resolve(1)
   })
}
 
promise().then(res=>{
    console.log(res)
})

Class类

  • 定义类:

class person {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

let p = new person("lisi", 18);
  • 类的修饰符: public private protected

// 使用public 修饰符 可以让你定义的变量 内部访问 也可以外部访问 如果不写默认就是public
class person {
  public name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

let p = new person("lisi", 18);
console.log(p.name);
console.log(p.age);

private 私有变量,只能在类内部使用

class person {
  public name: string;
  private age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

let p = new person("lisi", 18);
console.log(p.name);
console.log(p.age); // 属性“age”为私有属性,只能在类“person”中访问

protected 类的内部和子类访问

class person {
  public name: string;
  private age: number;
  protected height: number;
  constructor(name: string, age: number, height: number) {
    this.name = name;
    this.age = age;
    this.height = height;
  }
}

class man extends person {
  constructor(name: string, age: number, height: number) {
    super(name, age, height);
  }
}

interface 定义 类

抽象类

  1. 抽象类无法被实例化

abstract class A {
   public name:string
}
 
new A()  // 错误
  1. 定义的抽象方法必须在派生类实现

abstract class A {
   name: string
   constructor(name: string) {
      this.name = name;
   }
   print(): string {
      return this.name
   }
 
   abstract getName(): string
}
 
class B extends A {
   constructor() {
      super('lisi')
   }
   getName(): string {
      return this.name
   }
}
 
let b = new B();
 
console.log(b.getName());

元组类型

如果需要一个固定大小的不同类型值的集合,我们需要使用元组。

元组(Tuple)是固定数量的不同类型的元素的组合

let arr:[number,string] = [1,'string']
 
let arr2: readonly [number,boolean,string,undefined] = [1,true,'sring',undefined]

元组类型还可以支持自定义名称和变为可选的

let a:[x:number,y?:boolean] = [1]

越界元素

let arr:[number,string] = [1,'string']
 
arr.push(true)//error
arr.push(1)   // true

对于越界的元素他的类型被限制为 联合类型(就是你在元组中定义的类型)如下图

应用场景:定义excel返回的数据

let excel: [string, string, number, string][] = [
    ['title', 'name', 1, '123'],
    ['title', 'name', 1, '123'],
    ['title', 'name', 1, '123'],
    ['title', 'name', 1, '123'],
    ['title', 'name', 1, '123'],
]

枚举类型

1.数字枚举

例如 红绿蓝 Red = 0 Green = 1 Blue= 2 分别代表红色0 绿色为1 蓝色为2

//默认就是从0开始的 可以不写值
enum Types{
   Red,
   Green,
   BLue
}
console.log(Types.Red) // 0

增长枚举

enum Types{
   Red = 1,
   Green,
   BLue
}

Red使用初始化为 1。 其余的成员会从 1开始自动增长。 换句话说, Type.Red的值为 1, Green为 2, Blue为 3。

2.字符串枚举

enum Types{
   Red = 'red',
   Green = 'green',
   BLue = 'blue'
}

3.接口枚举

   enum Types {
      yyds,
      dddd
   }
   interface A {
      red:Types.yyds
   }
 
   let obj:A = {
      red:Types.yyds
   }

4.反向映射

它包含了正向映射( name -> value)和反向映射( value -> name)要注意的是 不会为字符串枚举成员生成反向映射。

enum Enum {
   fall
}
let a = Enum.fall;
console.log(a); //0
let nameOfA = Enum[a]; 
console.log(nameOfA); //fall

类型别名

type 关键字(可以给一个类型定义一个名字)多用于复合类型

定义类型别名

type str = string
let s:str = "lisi"
console.log(s);

定义函数别名

type str = () => string
let s: str = () => "lisi"
console.log(s);

定义联合类型别名

type str = string | number
let s: str = 123
let s2: str = '123'
console.log(s,s2);

type 和 interface 还是一些区别的 虽然都可以定义类型

1.interface可以继承 type 只能通过 & 交叉类型合并

2.type 可以定义 联合类型 和 可以使用一些操作符 interface不行

3.interface 遇到重名的会合并 type 不行

type高级用法

左边的值会作为右边值的子类型遵循图中上下的包含关系

type a = 1 extends number ? 1 : 0 //1   number 中包含 1
type a = 1 extends Number ? 1 : 0 //1
type a = 1 extends Object ? 1 : 0 //1
type a = 1 extends any ? 1 : 0 //1
type a = 1 extends unknow ? 1 : 0 //1
type a = 1 extends never ? 1 : 0 //0

never类型

TypeScript 将使用 never 类型来表示不应该存在的状态

// 返回never的函数必须存在无法达到的终点
// 因为必定抛出异常,所以 error 将不会有返回值
function error(message: string): never {
    throw new Error(message);
}

// 因为存在死循环,所以 loop 将不会有返回值
function loop(): never {
    while (true) {
    }
}

never 与 void 的差异

    //void类型只是没有返回值 但本身不会出错
    function Void():void {
        console.log();
    }
 
    //只会抛出异常没有返回值
    function Never():never {
        throw new Error('aaa')
    }
// 差异2   
type A = void | number | never
// 当我们鼠标移上去的时候会发现 只有void和number never在联合类型中会被直接移除

never 类型应用场景

type A = '小满' | '大满' | '超大满' 
 
function isXiaoMan(value:A) {
   switch (value) {
       case "小满":
           break 
       case "大满":
          break 
       case "超大满":
          break 
       default:
          //是用于场景兜底逻辑
          const error:never = value;
          return error
   }
}

比如新来了一个同事他新增了一个篮球,我们必须手动找到所有 switch 代码并处理,否则将有可能引入 BUG 。

而且这将是一个“隐蔽型”的BUG,如果回归面不够广,很难发现此类BUG。

那 TS 有没有办法帮助我们在类型检查阶段发现这个问题呢?

type A = '小满' | '大满' | '超大满' | "小小满"
 
function isXiaoMan(value:A) {
   switch (value) {
       case "小满":
           break 
       case "大满":
          break 
       case "超大满":
          break 
       default:
          //是用于场景兜底逻辑
          const error:never = value;
          return error
   }
}

由于任何类型都不能赋值给 never 类型的变量,所以当存在进入 default 分支的可能性时,TS的类型检查会及时帮我们发现这个问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值