【前端】Typescript入门

概述

TypeScript是由微软开发的自由和开源的编程语言。

TypeScript是JavaScript的一个超集,从今天数以百万计的JavaScript开发者所熟悉的语法和语义开始。可以使用现有的JavaScript代码,包括流行的JavaScript库,并从JavaScript代码中调用TypeScript代码。

TypeScript可以编译出纯净、 简洁的JavaScript代码,并且可以运行在任何浏览器上、Node.js环境中和任何支持ECMAScript 3(或更高版本)的JavaScript引擎中。

TypeScript的优势在于:它有更多的规则和类型限制,代码具有更高的预测性、可控性,易于维护和调试;对模块、命名空间和面向对象的支持,更容易组织代码开发大型复杂程序。

另外,TypeScript的编译步骤可以捕获运行之前的错误。

NPM安装TypeScript

如果你的本地环境已经安装了npm工具,可以使用以下命令来安装。

  • 使用国内镜像
npm config set registry https://registry.npmmirror.com
  • 安装TypeScript
npm install -g typescript

变量申明

基本类型

let isDone: boolean = false
let num: number = 1
let str: string = 'vue3js.cn'
let arr: number[] = [1, 2, 3] 
let arr2: Array<number> = [1, 2, 3] // 泛型数组
let obj: object = {
    
    }
let u: undefined = undefined;
let n: null = null;

类型补充

枚举 Enum

使用枚举类型可以为一组数值赋予友好的名字

enum LogLevel {
    
    
  info = 'info',
  warn = 'warn',
  error = 'error',
}

元组 Tuple

允许数组各元素的类型不必相同。 比如,你可以定义一对值分别为 string和number类型的元组

// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error

任意值 Any

表示任意类型,通常用于不确定内容的类型,比如来自用户输入或第三方代码库

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

空值 Void

与 any 相反,通常用于函数,表示没有返回值

function warnUser(): void {
    
    
    console.log("This is my warning message");
}

类型注解

类型注解在TypeScript中是记录函数或变量约束的简便方法。在这个示例中,我们想要在调用greeter函数时传入一个字符串类型参数。我们可以尝试在调用greeter函数时变为传入一个数组:

function greeter(person: string){
    
    
return"Hello, " + person;
}
var user = [0, 1 , 2];
document.body.innerHTML = greeter(user);

重新编译,将看到一个错误:

greeter.ts(7,26): Supplied parameters do not match any signature of call target

同样,在调用greeter函数时尝试不传入任何参数。TypeScript将会告诉你调用这个函数时需要带一个参数。在这两个示例中,TypeScript基于你的代码结构和类型注解可以提供静态分析。

注意,虽然有错误,但是仍然编译创建了greeter.js文件。即使你的代码中有错误,你仍旧可以使用TypeScript。但是在这种情况,TypeScript会发出警告:你的代码可能不能按照你预想的那样运行。

交叉类型

交叉类型(Intersection Types),将多个类型合并为一个类型

interface foo {
    
    
    x: number
}
interface bar {
    
    
    b: number
}
type intersection = foo & bar
const result: intersection = {
    
    
    x: 10,
    b: 20
}
const result1: intersection = {
    
    
    x: 10
}  // error

联合类型

联合类型(Union Types),表示一个值可以是几种类型之一。 我们用竖线 | 分隔每个类型,所以 number | string | boolean表示一个值可以是 number, string,或 boolean

type arg = string | number | boolean
const foo = (arg: arg):any =>{
    
     
    console.log(arg)
}
foo(1)
foo('2')
foo(true)

函数重载

函数重载(Function Overloading), 允许创建数项名称相同但输入输出类型或个数不同的子程序,可以简单理解为一个函数可以执行多项任务的能力

例我们有一个add函数,它可以接收string类型的参数进行拼接,也可以接收number类型的参数进行相加

function add (arg1: string, arg2: string): string
function add (arg1: number, arg2: number): number

// 实现
function add <T,U>(arg1: T, arg2: U) {
    
    
  // 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 arg1 + arg2
  if (typeof arg1 === 'string' && typeof arg2 === 'string') {
    
    
    return arg1 + arg2
  } else if (typeof arg1 === 'number' && typeof arg2 === 'number') {
    
    
    return arg1 + arg2
  }
}

add(1, 2) // 3
add('1','2') //'12'

接口

让我们进一步开发我们的demo。 在这里我们使用一个接口,它描述了具有firstName和lastName字段的对象。在TypeScript中,如果两个类型其内部结构兼容,那么这两种类型兼容。这使我们实现一个接口,仅仅只需必要的结构形状,而不必有明确的implements子句。

 interface  Person {
    
    
    firstName:  string ;
    lastName:  string ;
}
  function   greeter ( person: Person )  {
    
    
     return   "Hello, "  + person.firstName +  " "  + person.lastName;
}
 var  user = {
    
     firstName:  "Jane" , lastName:  "User"  };
 document .body.innerHTML = greeter(user);

类型契约,跟我们平常调服务端接口要先定义字段一个理

如下例子 point 跟 Point 类型必须一致,多一个少一个也是不被允许的

interface Point {
    
    
    x: number
    y: number
    z?: number
    readonly l: number
}
const point: Point = {
    
     x: 10, y: 20, z: 30, l: 40 }
const point2: Point = {
    
     x: '10', y: 20, z: 30, l: 40 } // Error 
const point3: Point = {
    
     x: 10, y: 20, z: 30 } // Error 
const point4: Point = {
    
     x: 10, y: 20, z: 30, l: 40, m: 50 } // Error 

可选与只读 ? 表示可选参, readonly 表示只读

const point5: Point = {
    
     x: 10, y: 20, l: 40 } // 正常
point5.l = 50 // error

最后,让我们最后一次使用类来继续开发demo。TypeScript支持新的JavaScript特性,像基于类的面向对象编程的支持。

在这里,我们创建一个具有构造函数和一些公共字段的Student类。注意:类和接口的良好配合使用,决定一个程序员的抽象水平。

此外,在构造函数参数中使用public是一种简写形式,它将自动创建具有该名称的属性。

 class   Student  {
    
    
    fullName:  string ;
    constructor( public  firstName,  public  middleInitial,  public  lastName) {
    
    
         this .fullName = firstName + " " + middleInitial +  " "  + lastName;
    }
}
 interface   Person  {
    
    
    firstName:  string ;
    lastName:  string ;
}
 function  greeter ( person : Person )  {
    
    
     return   "Hello, "  + person.firstName +  " "  + person.lastName;
}
 var  user =  new  Student( "Jane" ,  "M." ,  "User" );
document.body.innerHTML = greeter(user);

再次运行tsc greeter.ts,你将看到生成的JavaScript代码和以前的一样。TypeScript中的类只是对于经常在JavaScript中使用了相同的基于原型的面向对象的简写。

泛型

泛型的意义在于函数的重用性,设计原则希望组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型

比如根据业务最初的设计函数 identity 入参为String

function identity(arg: String){
    
    
	return arg
}
console.log(identity('100'))

业务迭代过程参数需要支持 Number

function identity(arg: String){
    
    
	return arg
}
console.log(identity(100)) // Argument of type '100' is not assignable to parameter of type 'String'.

为什么不用any呢?

使用 any 会丢失掉一些信息,我们无法确定返回值是什么类型
泛型可以保证入参跟返回值是相同类型的,它是一种特殊的变量,只用于表示类型而不是值

语法 (arg:T):T 其中T为自定义变量

const hello : string = "Hello vue!"
function say<T>(arg: T): T {
    
    
    return arg;
}
console.log(say(hello)) // Hello vue! 

泛型约束
我们使用同样的例子,加了一个console,但是很不幸运,报错了,因为泛型无法保证每种类型都有.length 属性

const hello : string = "Hello vue!"
function say<T>(arg: T): T {
    
    
	console.log(arg.length) // Property 'length' does not exist on type 'T'.
    return arg;
}
console.log(say(hello)) // Hello vue! 

从这里我们也又看出来一个跟any不同的地方,如果我们想要在约束层面上就结束战斗,我们需要定义一个接口来描述约束条件

interface Lengthwise {
    
    
    length: number;
}

function say<T extends Lengthwise>(arg: T): T {
    
    
	console.log(arg.length)
    return arg;
}
console.log(say(1))  // Argument of type '1' is not assignable to parameter of type 'Lengthwise'.
console.log(say({
    
    value: 'hello vue!', length: 10})) // { value: 'hello vue!', length: 10 } 

总结

下面我们来看看在Vue源码Typescript是如何书写的,这里我们以defineComponent函数为例,大家可以通过这个实例,再结合文章的内容,去理解,加深Typescript的认识

// overload 1: direct setup function
export function defineComponent<Props, RawBindings = object>(
  setup: (
    props: Readonly<Props>,
    ctx: SetupContext
  ) => RawBindings | RenderFunction
): {
    
    
  new (): ComponentPublicInstance<
    Props,
    RawBindings,
    {
    
    },
    {
    
    },
    {
    
    },
    // public props
    VNodeProps & Props
  >
} & FunctionalComponent<Props>

// defineComponent一共有四个重载,这里省略三个

// implementation, close to no-op
export function defineComponent(options: unknown) {
    
    
  return isFunction(options) ? {
    
     setup: options } : options
}

来源

Typescript
TypeScript 快速入门

猜你喜欢

转载自blog.csdn.net/weixin_44231544/article/details/134706716