TypeScript应用

前言

JavaScript是一种弱类型(或称动态类型)语言,即变量的类型是不确定的。如:
let x = 5; // 5
x = x + 'A'; // '5A'  进行了隐式类型转换
类型完全由当前的值决定,这样看起来十分灵活,代码也简洁,但是对于大型项目来说,强类型更有利,可以降低系统的复杂性。
// JS
function getData(url, params) {
    ...
}

// TS
interface ParamInterface {
    id: string;
    name: string;
}
function getData(url:string, param: ParamInterface) {
    ...
}

现有的能让javascript转成强类型语言有几种解决方案:TypeScript、FlowCheck、Flow(Flow,Facebook在2014年发布的一个类型检查工具,用来检查React的源码,也引入了Flow做静态类型检查)

TypeScript介绍

基础类型
接口

函数
泛型
高级类型

基础类型
布尔值
let isDone:boolean = false;
数字
let decLiteral:number = 6; // 十进制
let hexLiteral:number = 0xfood; // 十六进制
let binaryLiteral:number = 0b1010; // 二进制
let octalLiteral:number = 0o744; // 八进制
  • 十进制:没有前导0的数值
  • 八进制:有前缀0o或0O的数值,或者有前导0且只用到0-7的八个阿拉伯数字的数值
  • 十六进制:有前缀0x或0X的数值
  • 二进制:有前缀0b或0B的数值
数组
let list:number[] = [1,2,3];
// 或者数组泛型
let list: Array<number> = [1,2,3];
// 混合数组
let list:number[] = [1, 2, 3, 'a', 'b']; // 报错Type 'string' is not assignable to type 'number'
// 可使用联合类型来自定义
let list: (string|number)[] = [1, 2, 3, 'a', 'b'];
元组 Tuple
let list: [string, string, number] = ['张三', '男', 20];
// 使用场景
let list: [string,string,number][] = [
    ['张三', '男', 20],
    ['李四', '男', 21],
    ['王五', '男', 22],
]
枚举
if (res.data === '0') {
    // 处理操作1
} else if (res.data === '1') {
    // 处理操作2
}

// 换成枚举
enum Status {on, off}
if (res.data === Status.on) {
    // 处理操作1
} else if (res.data === Status.off) {
    // 处理操作2
}

// 可以是数字枚举或者是字符串枚举
enum Direction {
    Up,
    Down,
    Left,
    Right,
}
enum Direction {
    Up = 1,
    Down,
    Left,
    Right,
}
enum Direction {
    Up = 'up',
    Down = 'down',
    Left = 'left',
    Right = 'right',
}
// ts中的枚举类型和普通的JS对象本质上没有区别,
// 只是对于开发者来说,相较于直接使用值去做判断,枚举类型更易读,能够提升代码的可读性和易维护性
Any
// 不清楚变量具体是哪个类型的时候使用any,类型检查器不会对这些值进行检查而是直接让它们通过编译阶段
let notSure: any = 4;
notSure = 'maybe a string instead';
let list: any[] = [1, true, 'free'];
Void
// void类型像是与any类型相反,它表示没有任何类型,主要用在函数上
function warnUser():void {
    console.log('This is my warning message');
}
Null和Undefined
let u: undefined = undefined;
let n: null = null;
Never
// never类型表示的是那些永不存在的值的类型,never类型常用在会抛出异常或根本就不会有返回值的函数中
function error(message: string):never {
    throw new Error(message);
}
Object

表示非原始类型,也就是除number,string,boolean,symbol,null或undefined之外的类型

类型断言
// 操作any类型的变量的时候,大多数情况都会用类型断言
let someValue: any = 'this is a string';
// 用法一:尖括号
let strLength: number = (<string>someValue).length;
// 用法二:as
let strLength: number = (someValue as string).length;
接口

TypeScript的核心原则之一是对值的结构进行类型检查,而接口可以对这些值的结构约束,包括对象、函数以及类的结构和类型。
描述普通对象

interface PersonInterface {
    name: string,
    age: number,
}
let person: PersonInterface = {
    name: '张三',
    age: 18,
    gender: '男', // 加这行报错
}
// 类型兼容性
const person1 = {
    name: '张三',
    age: 18,
    gender: '男',
}
person: PersonInterface = person1;
// 可选属性
interface PersonInterface {
    name: string,
    age?: number,
}
person: personInterface = {
    name: '张三',
}
// 只读属性
interface PersonInterface {
    readonly name: string,
    readonly age: number,
}
person: PersonInterface = {
    name: '张三',
    age: 18,
}
person.age = 20; // 报错

// 字符串索引签名,可以有任意数量的属性
interface PersonInterface {
    name: string,
    age: number,
    [propName: string]: any
}
person: PersonInterface = {
    name: '远扬',
    age: 18,
    gender: 'F',
    phone: '2312'
}
索引签名
// 数字索引
// 表示了当用number去索引StringArray时会得到string类型的返回值
interface StringArray {
    [index:number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr:string = myArray[0];

// 字符串索引
interface ScoreMap = {
    [subject:string]: number;
}
let data:ScoreMap = {
    age: 18,
    height: 178,
}

// 数字索引和字符串索引相结合,数字索引的返回值必须是字符串索引值返回值类型的子类型
interface ScoreMap {
    [index:number]:string;
    [name:string]:string;
}
let data: ScoreMap = {
    0: 'index',
    hobby: 'play',
}
描述函数类型
interface SearchFunc {
    (source:string,subString:string):boolean;
}
let mySearch: SearchFunc;
mySearch = function(src:string,sub:string) {
    let result = source.search(subString);
    return result > -1;
}
描述类类型
interface test {
    name: string,
    value: number,
}
// 实现接口,接口描述了类的公共部分
class TestClass implementes test {
    name: string;
    value: number;
    age: number;
    
    constructor(name:string, value:number, age:number) {
        this.name = name;
        this.value = value;
        this.age = age;
    }
}
继承接口
interface Shape {
    color: string;
}
interface Square extends Shape {
    sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
混合类型
// 可以同时作为函数和对象使用,并带有额外的属性
interface Counter {
    (start: number): string;
    interval: number;
    reset():void;
}
function getCounter():Counter{
    let counter = <Counter>function(start:number) {};
    counter.interval = 123;
    counter.reset = function(){};
    return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

从ECMAScript6开始,支持基于类的面向对象的方式

// 声明一个Greeter类。这个类有3个成员:一个叫做greeting的属性,一个构造函数和一个greet方法
class Greeter {
    greeting: string;
    constructor(message:string) {
        this.greeting = message;
    }
    greet() {
        return 'Hello ,' + this.greeting;
    }
}
let greeter = new Greeter("World");
继承

通过extends关键字实现继承

class Animal {
    name: string;
    constructor(theName:string) {
        this.name = theName;
    }
    move(distanceInMeters: number = 0) {
        console.log(`Animal moved ${distanceInMeters}m.`);
    }
}
// 继承Animal类
class Dog extends Animal {
    constructor(name:string) {
        super(name);   // 构造函数里访问this的属性之前,一定要先调用super()
        console.log('name', this.name);
    }
    bark() {
        console.log('Woof! Woof!');
    }
}
// 创建一个Dog的实例
const dog = new Dog('mike');
dog.bark();
dog.move(10);
dog.bark();
修饰符

公有,私有与受保护的(默认)修饰符:public、private、protected

// private
class Animal {
    private name: String;
    constructor(theName:string) {this.name = theName;}
}
class Rhino extends Animal {
    constructor(name:string) {super(name);}
    sayName(){
        console.log(this.name);  // 错误: 'name'是父类私有的
    }
}
const cat = new Rhino('Cat');
cat.name; // 错误:'name'是私有的
cat.sayName();  // 错误:'name'是私有的

// protected
class Animal {
    protected name: string;
    constructor(theName:string) {this.name = theName;}
}
class Rhino extends Animal {
    constructor(name:string) {super(name);}
    sayName(){
        console.log(this.name); // 不会报错
    }
}
const cat = new Rhino('Cat');
cat.name; // 错误:name属性是被保护的,只有在本类或者派生类中才能访问
cat.sayName(); // 不会报错
静态属性

当类被实例化的时候才会被初始化的属性

class Grid {
    static origin = {x:0,y:0};
    staticValue(){
        console.log(Grid.origin.x); // 访问静态属性的时候,要前面加上类名
    }
    construtor(public scale:number) {}
}
抽象类

抽象类的抽象方法不包含具体实现并且必须在派生类中实现,主要关键字abstract

abstract class AbstractTest {
    constructor(public name:string) {
        
    }
    abstract printMeeting():void; // 必须在派生类中实现
}
class Test extends AbstractTest {
    constructor(){
        super('test'); // 在派生类的构造函数中必须调用super()
    }
    printMeeting(): void {
        console.log('generateReports');
    }
}
let test:AbstractTest; // 允许创建一个对象类型的引用
test = new AbstractTest(); // 错误:不能创建一个抽象类的实例
test = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值
test.printMeeting();
test.generateReports(); // 错误:方法在声明的抽象类中不存在
函数

定义类型

// 参数类型和返回值类型
function add(x:number, y:number):number{
    return x+y;
}
// 可选参数
function buildName(firstName: string, lastName?:string) {
    if(lastName)
        return `${firstName}${lastName}`;
    else
        return firstName;
}
// 参数默认值
function buildName(firstName:string, lastName = 'Smith') {
    return `${firstName}${lastName}`;
}
// 剩余参数restOfName会被当做个数不限的可选参数。可以一个都没有,同样也可以有任意个
function buildName(firstName:string, ...restOfName:string[]) {
    console.log(restOfName); // ["Samuel","Lucas","MacKinzie"]
    return `${firstName}${restOfName.join(" ")}`;
}
const employeeName = buildName("Joseph","Samuel","Lucas","MacKinzie");
泛型

泛型是指在定义函数、接口或者类的时候,不预先指定其类型,而是在使用的时候再手动制定,这样做的好处是可兼容多种数据类型,提高代码的复用率

// 举个简单的例子,定义一个函数,参数和返回值都是Number的类型,如果后面想改为数组、字符串的话,还要另建一个函数,可以看出该函数并不是可扩展或通用的
function identity(value:Number):Number{
    return value;
}
// 使用类型变量T,可以捕获用户传入的类型
function identity<T>(arg:T):T {
    return arg;
}
let output = identity<string>("myString");
// 或者
let output = identity("myString");
例子: const f = x=>x,其中f是泛型。由用户传入的数据决定是什么类型。
泛型变量

泛型中必须正确地使用这个通用的类型,即必须把这些参数当做是任意或所有类型,不能把参数当做特定类型去操作

function loggingIdentity<T>(arg:T):T{
    console.log(arg.length);   // Error:T doesn't have .length
    return arg;
}
// 泛型数组
function loggingIdentity<T>(arg:T[]):T[] {
    console.log(arg.length);   // Array has a .length,so no more error
    return arg;
}
泛型类型

定义一个泛型类型的变量

function identity<T>(arg:T):T{
    return arg;
}
let myIdentity: <T>(arg:T) => T = identity;

// 使用接口定义
interface GenericIdentityFn<T>{
    (arg:T):T;
}
function identity<T>(arg:T):T{
    return arg;
}
let myIdentity:GenericIdentityFn<string>=identity;
泛型类
class GenericNumber<T,U>{
    name: T;
    add: (x:U, y:U) => U;
}
let myGenericNumber = new GenericNumber<string,number>();
myGenericNumber.name = 'hello world';
myGenericNumber.add = function(x,y) {return x + y;}
泛型约束
function loggingIdentity<T>(arg:T):T {
    console.log(arg.length); // 报错,T类型没有length属性
    return arg;
}
// 使用接口约束泛型,T类型必须有length属性
interface Lengthwise {
    length: number;
}
function loggingIdentity<T extends Lengthwise>(arg:T):T{
    console.log(arg.length); // 没问题
    return arg;
}
loggingIdentity(3); // 报错,数字3没有length属性
高级类型
交叉类型、联合类型、类型别名
interface A {
    x: number;
    y: number;
}
interface B {
    y: number;
    z: number;
}
// 使用类型别名可以自定义自己想要的类型,起别名不会新建一个类型 - 它只是创建了一个新名字来引用那个类型
// 交叉类型U具有x,y,z三个属性
type U = A & B;

// 联合类型I只有y这个共有属性
type I = A | B;

Vue结合TypeScript

项目配置

tsconfig.json

{
  "compilerOptions": { // 编译选项
    "target": "esnext", // 指定ECMAScript目标版本,这里选esnext是可以将ES6还在草案规范的语法转成今天的语法
    "module": "esnext", // 指定生成哪个模块系统代码
    "strict": true, // 启用所有严格类型检查选项
    "jsx": "preserve", // jsx有三个值:preserve,react和react-native
    "importHelpers": true,
    "moduleResolution": "node", // 如何处理模块,使用node模块解析机制
    "experimentDecorators": true, // 启用实验性的装饰器特性
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "noImplicitAny": false, // 在表达式和声明上有隐含的any类型时报错。如果改为true,any类型的都会报错
    "noImplicitThis": false, // 当this表达式的值为any类型的时候,生成一个错误
    "suppressImplicitAnyIndexErrors": true,
    "sourceMap": true, // 生成相应的.map文件
    "baseUrl": ".", // 解析非相对模块名的基准目录
    "resolveJsonModule": true,
    "types": [ // 类型声明文件名列表
      "webpack-env" // webpack类型定义
    ],
    "paths": { // 路径映射的列表
      "@/*": [
        "src/*"
      ],
      "@store/*": [
        "src/store/*"
      ],
      "@components/*": [
        "src/components/*"
      ],
      "@utils/*": [
        "src/utils/*"
      ],
      "@assets/*": [
        "src/assets/*"
      ]
    },
    "lib": [ // 编译过程中需要引入的库文件的列表
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [ // 编译文件列表
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [ // 无需变异文件列表
    "node_modules",
    "src/components/create-form"
  ]
}
装饰器

装饰器是一种函数,写成@+函数名。它可以放在类和类方法的定义前面,是一种与类(class)相关的语法,用来注释或修改类和类方法。动态地给对象添加额外的职责。在不改变接口的前提下,增强类的性能
举个例子:

@testable
class MyTestableClass {
    // ...
}

function testable(target) {
    target.isTestable = true;
}

MyTestableClass.isTestable // true

@testable就是一个装饰器。它修饰了MyTestableClass这个类的行为,为它加上了静态属性isTestable。testable函数的参数target是MyTestableClass类本身装饰器不仅可以装饰类,还可以装饰类的属性、方法。

vue-property-decorator

vue-property-decorator在vue-class-component的基础上增加了更多与Vue相关的装饰器,使Vue组件更好地跟TS结合使用。

  • @Component
    @Component装饰器可以接收一个对象作为参数,可以在对象中声明components、filters、directives等未提供装饰器的选项

  • @Emit
    指定事件emit,可以使用此修饰符,接收一个可选参数,该参数是$Emit的第一个参数,充当事件名。如果没有提供这个参数,$Emit会将回调函数名的camelCase(驼峰命名)转为kebab-case(短横线命名),并将其作为事件名,它会将回调函数的返回值作为第二个参数,在返回值之后,回调函数的参数也会一起被当做参数使用,也可以直接使用this.$emit()

  • @Prop
    接收一个参数,这个参数可以有三种写法

  • @Inject
    指定依赖注入

  • @Provide
    指定Provide

  • @Watch
    可接收2个参数,第一个参数是要监听的属性,第二个参数是对象,可配置deep、immediate属性
    举个例子

import { Component, Prop, Watch, Vue } from 'vue-property-decorator'
@Component({
    components: {
        // 注册业务组件
    },
})
export class MyComponent extends Vue {
    dataA: string = 'test'
    count = 0
    @Prop(String) readonly name!string | undefined; // 指定prop的类型
    @Prop({default: 30, type: Number}) private age!: number; // type、default、required、validator
    @Prop([String, Boolean]) private sex!: string | boolean; // 指定prop的可选类型
    @Emit('reset')
    resetCount() {
        this.count = 0
    }
    @Emit()
    returnValue(){
        return 10
    }
    @Emit()
    onInputChange(e) {
        return e.target.value
    }
    
    // watcher
    @watch('child')
    onChildChanged(val:string, oldVal:string) {}
    
    @watch('person', { immediate: true,deep:true })
    onPersonChanged(val:Person, oldVal:Person) {}
}

解析之后会变成

export default {
    data() {
        return {
            dataA: 'test'
        }
    },
    props: {
        propA: {
            type: Number
        },
        propB: {
            type: Array,
            default: [10, 20, 30, 50]
        },
        propC: {
            type: String,
            default: 'total, sizes, prev, next, jumper'
        }
    },
    watch:{
        'child': {
            // handler:其值是一个回调函数。即监听到变化时应该执行的函数。
            handler: 'onChildChanged',
            
            // immediate:其值是true或false;
            // immeditate:true代表立即先去执行里面的handler方法
            // immeditate:false就跟我们以前的效果一样,不会在绑定的时候就执行
            immediate: false,
            
            // deep:其值是true或false;确认是否深入监听
            // deep的意思就是深入观察,监听器会一层层地往下遍历,给对象的所有属性都加上这个监听器
            // 受现代JavaScript的限制(以及废弃Object.observe),Vue不能检测到对象属性的添加或删除
            deep: false
        },
        'person': {
            handler: 'onPersonChanged',
            immediate: true,
            deep: true
        }
    },
    methods: {
        resetCount(){
            this.count = 0;
            this.$emit('reset')
        },
        returnValue() {
            this.$emit('return-value', 10)
        },
        onInputChange(e) {
            this.$emit('on-input-change', e.target.value, e)
        },
        onChildChanged(val, oldVal) {},
        onPersonChanged(val, oldVal) {}
    }
}

mixins的用法

import {
    Vue,
    Component,
    Watch,
    Inject,
    Prop,
    Mixins
} from 'vue-property-decorator';
import myMixin from './myMixin';
export default class BaseCheck extends Mixins(myMixin) {
    
}

// myMixin.ts
import {Vue, Component} from 'vue-property-decorator';

@Component({})
export default class LanMixin extends Vue {

}
声明文件

模块导出声明、全局类型声明放在描述文件*.d.ts

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
TypeScriptJavaScript的一个超集,它支持ECMAScript 6(ES6)标准,由微软开发并且是自由和开源的编程语言。TypeScript的设计目标是用于开发大型应用,它可以被编译成纯JavaScript,并且生成的JavaScript代码可以在任何浏览器上运行。在安装TypeScript之后,可以使用命令行工具进行编译和检测安装。通过手动编译TS代码,可以将TypeScript代码转换为JavaScript代码。通过在HTML文件中引用生成的JavaScript文件,可以在浏览器中测试运行TypeScript应用TypeScript相对于JavaScript的一个优点是它是强类型语言,这意味着变量的类型在声明时就是确定的。对于大型项目来说,强类型语言可以降低系统的复杂性。在TypeScript中,可以使用接口来定义参数的类型,以提供更好的类型检查和代码提示。另外,TypeScript还支持类型断言,可以用于操作任何类型的变量。 总而言之,TypeScript是一种用于开发大型应用的编程语言,它具有与JavaScript兼容的语法,并且提供了类型检查和代码提示等增强功能。它可以通过编译生成纯JavaScript代码,并且可以在任何浏览器上运行。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [TypeScript 简单使用](https://blog.csdn.net/CRJ453027119/article/details/127206972)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [TypeScript应用](https://blog.csdn.net/qq_38361229/article/details/128906019)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值