TypeScript(六)条件类型,函数,装饰器

条件类型

TypeScript 中的条件类型是一种高级类型,它使我们根据不同的条件创建更复杂的类型。
TS中的条件类型就是在类型中添加条件分支,以支持更加灵活的泛型

条件类型允许我们根据某个类型的属性是否满足某个条件,来确定最终的类型。

type IsString<T> = T extends string ? "yes" : "no";let result4: IsString<"hello"> = "yes";  // 类型为 "no"
let result3: IsString<42> = "no";   // 类型为 "yes"
console.log(result4, result3); // yes no
// let result2: IsString<"hello"> = "no";  // 类型为 "no"
// let result1: IsString<42> = "yes";   // 类型为 "yes"
// 通过给泛型参数传递不同的类型,得到了不同的结果。

在这里插入图片描述

  1. infer 关键字

infer 关键字用于引入一种类型变量,定义泛型里面推断出来的类型参数,而不是外部传入的类型参数。
通常,infer 关键字结合条件类型使用,用于提取和推断类型信息。

// infer R 表示 R 类型是 TypeScript 自己推断出来的,不用显式传入。
// 传入的 T 是函数, 就返回这个函数的结果类型。
type ExtractReturnType<T> = T extends (...args: any[]) => infer R ? R : never;function sum(a: string, b: string): string {return a + b;
}
const result1: ExtractReturnType<typeof sum> = 'hello'; // 编译通过
console.log(result1) // hello
  • tuple转union 元组转为联合类型
type ElementOf<T> = T extends Array<infer P> ? P : never;
type Tuple = [string, number];
type TupleToUnion = ElementOf<Tuple>; 
// 等同于
// type TupleToUnion = string | numberlet arr: TupleToUnion = '迪西'
let arr2: TupleToUnion = 9
  • 联合类型转成交叉类型 string | number =》string & number
type T1 = { name: string };
type T2 = { age: number };
type ToIntersection<T> = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U : never;
// 由于U需要同时满足T1的定义、T2的定义,因此U需要包含T1、T2所有的类型,因此T3就是T1 & T2
type T3 = ToIntersection<{ a: (x: T1) => void, b: (x: T2) => void }>; // type T3 = T1 & T2let obj: T3 = {name: '迪西',age: 9,
}
  1. 条件类型中的泛型推断
    条件类型中的泛型推断是一个重要的概念,
    // 条件类型中的泛型推断
    type ExtractType<T> = T extends infer U ? U : never;
    // 使用泛型推断的条件类型
    type ExtractedType = ExtractType<string[]>;
    // 查看结果类型
    let extractedValue: ExtractedType = ["1", "2"];
    
    在这里插入图片描述
  2. 分布式条件类型
    条件类型在处理联合类型时表现出一种分布式的行为,
    type Diff<T, U> = T extends U ? never : T; // 返回T 不属于U的子类型的部分
    type Filter<T, U> = T extends U ? T : never; // 返回T 属于U的子类型的部分type OnlyStrings = Diff<"a" | "b" | 1 | 2 | false, number | boolean>; // "a" | "b" | false
    type OnlyNumbers = Filter<"a" | "b" | 1 | 2 | true, number | boolean>; // 1 | 2 | truelet str1: OnlyStrings = 'a'
    let str2: OnlyNumbers = 1
    
  3. 内置条件类型
    在 TypeScript 中,有一些内置的条件类型,比如 NonNullable,它从类型中排除了 null 和 undefined。这里确保 value 的类型是非空的字符串。
    type NonNullableType<T> = T extends null | undefined ? never : T;
    const value1: NonNullableType<string | null> = '迪西'; // 编译通过
    // const value2: NonNullableType<string | null> = null; // 编译通过
    
    在这里插入图片描述
  4. 分配条件类型
    分布式条件类型是指在联合类型上进行条件类型判断时,该判断会分布到联合类型的每个成员上,从而生成新的联合类型。
    type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
    // MyReturnType ,应用于函数类型和非函数类型。由于条件类型的分布式能力,它会分别应用于函数类型和非函数类型的每个成员,最终生成新的联合类型
    function sum(a: number):number {return a;
    }
    // 编译通过 nonFuncResult 类型会被推断为string
    const nonFuncResult: MyReturnType<string> = "Hello";
    // 编译通过 funcResult 类型会被推断为number
    const funcResult: MyReturnType<typeof sum> = 3; 
    
  5. 条件类型在映射类型中的应用
    映射类型是一种用于从现有类型创建新类型的工具,可以根据对象的属性是否满足条件来创建一个新类型,从而对现有类型的属性进行转换、修改或过滤。
    type MakeNullable<T> = {[K in keyof T]: T[K] extends number ? T[K] | null : T[K];
    };// 使用映射类型中的条件类型
    type User = {id: number;name: string;age: number;
    };type NullableUser = MakeNullable<User>;
    // 将 T 中的 number 类型属性改为可空。通过应用于 User 类型,我们得到了一个可空的 NullableUser 类型。
    // 查看结果类型
    let nullableUser: NullableUser = {id: 1,name: "John",age: null,
    };
    
    在这里插入图片描述
  6. 条件类型中的嵌套应用
    条件类型可以嵌套使用,形成更复杂的类型判断。
    // 嵌套应用的条件类型
    type FlattenArray<T> = T extends Array<infer U> ? FlattenArray<U> : T;// 使用嵌套应用的条件类型
    type NestedArray = ['迪西', ['小波', ['拉拉', '嘻嘻'], '哈哈']];type FlatArray = FlattenArray<NestedArray>;// 查看结果类型
    let flatArray: FlatArray = '迪西';
    
    在这里插入图片描述

函数

  1. 函数是JavaScript应用程序的基础。 它帮助你实现抽象层,模拟类,信息隐藏和模块。
  2. 和JavaScript一样,TypeScript函数可以创建有名字的函数和匿名函数。可以随意选择适合应用程序的方式,不论是定义一系列API函数还是只使用一次的函数。
// Named function
function add(x, y) {return x + y;
}
// 匿名函数 函数表达式是一种将函数赋值给变量的方式
let myAdd = function(x, y) { return x + y; };
  1. 函数类型
    函数类型包含2部分:参数类型和返回值类型。 当写出完整函数类型的时候,这两部分都是需要的
  • 参数类型
    为每个参数指定一个名字和类型。 这个名字只是为了增加可读性。
    只要参数类型是匹配的,那么就认为它是有效的函数类型,而不在乎参数名是否正确。
    let myAdd: (baseValue: number, increment: number) => number =function(x: number, y: number): number { return x + y; };
    
  • 返回值类型
    返回值,我们在函数和返回值类型之前使用( =>)符号,使之清晰明了。
    返回值类型是函数类型的必要部分,如果函数没有返回任何值,你也必须指定返回值类型为 void而不能留空。
    let myAdd = function(x: number, y: number): number { return x + y; };
    
  1. 可选参数和默认参数
    使用?符号来定义可选参数。可选参数可以不传递
    在调用这些函数时,我们可以选择传递或不传递可选参数。
    // 1).可选参数 ?代表参数可选,age可传可不传
    function func1(name: string, age?: number): void {console.log(name); // 张三
    }
    func1('张三');// 2).可选参数,参数有默认值
    function func2(url: string, method: string = 'POST'): void {console.log(url, method); // /list POST
    }
    func2('/list');
    
  2. 剩余参数
    可以使用…符号来定义剩余参数。剩余参数可以接受任意数量的参数,并将它们作为数组传递给函数
    function func1(...arge: number[]): number {return arge.reduce((val, item) => val + item, 0);
    }
    console.log(func1(4, 5, 6, 7)); // 22
    
  3. 函数重载
    函数重载是一种在 TypeScript 中定义多个具有相同名称但参数类型和数量不同的函数的技术。这可以编写更清晰和类型安全的代码。
let obj: any = {};
function func1(val: string): void;
function func1(val: number): void;
function func1(val: boolean): void;
function func1(val: any): void {if (typeof val === 'string') {obj.name = val;} else if (typeof val === 'number') {obj.age = val;} else if (typeof val === 'boolean' ) {obj.isSuccess = val;}
}
func1('迪西');
func1(10);
func1(true);
console.log(obj); // {name: '迪西', age: 10, isSuccess: true}

装饰器

要启用实验性的装饰器特性,你必须在命令行或tsconfig.json里启用experimentalDecorators编译器选项:

tsc --target ES5 --experimentalDecorators

tsconfig.json:

{"compilerOptions": {"target": "ES5","experimentalDecorators": true}
}

解决bug

编译ts文件报错:Unable to resolve signature of method decorator when called as an expression.
官网的配置还是不能解决报错
可以通过监ts文件的热更新。
$ tsc 文件名 --target ES5 -w --experimentalDecorators

装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。 装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

装饰器的作用
1)只能在类中使用
2)减少冗余代码量
3)提高代码扩展性

  1. 装饰器的语法
    装饰器本质上就是一个函数,在特定的位置调用装饰器函数即可对数据进行扩展。
    // target 表示要扩展的数据,可以是类、方法、属性、参数等。
    function myDecorator(target: any) {// 对 target 进行处理
    }
    
  2. 类装饰器
    类装饰器在类声明之前被声明(紧靠着类声明)。 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。 类装饰器不能用在声明文件中( .d.ts),也不能用在任何外部上下文中
    类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
    如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。

    注意 如果你要返回一个新的构造函数,你必须注意处理好原来的原型链。 在运行时的装饰器调用逻辑中 不会为你做这些。

    function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {return class extends constructor {newProperty = "new property";hello = "override";}
    }@classDecorator
    class Greeter {property = "property";hello: string;constructor(m: string) {console.log(m) // worldthis.hello = m;}
    }console.log(new Greeter("world")); // class_1 {property: 'property', hello: 'override', newProperty: 'new property'}
    
  3. 装饰器工厂
    若需要要定制一个修饰器如何应用到一个声明上,得写一个装饰器工厂函数。 装饰器工厂就是一个简单的函数,它返回一个表达式,以供装饰器在运行时调用。
    function color(value: string) { // 这是一个装饰器工厂return function (target) { //  这是装饰器// do something with "target" and "value"...}
    }
    
    function addNameEatFactory(name: string):Function {return function addNameEat(constructor: Function):void {constructor.prototype.name = name;constructor.prototype.eat = (m) => {console.log(m) // 2};};
    };
    @addNameEatFactory('Jerry')
    class Person {name: string;eat: Function | undefined;constructor(n) {console.log(n) // 1this.name = n;}
    }
    let p: Person = new Person(1);
    console.log(p.name); // 1
    p.eat(2);
    
  4. 属性、方法装饰器
    方法装饰器声明在一个方法的声明之前(紧靠着方法声明)。 它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。 方法装饰器不能用在声明文件( .d.ts),重载或者任何外部上下文(比如declare的类)中。
    方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

    1)对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
    2)成员的名字。
    3)成员的属性描述符。
    注意  如果代码输出目标版本小于ES5,属性描述符将会是undefined。
    如果方法装饰器返回一个值,它会被用作方法的属性描述符。
    注意  如果代码输出目标版本小于ES5返回值会被忽略。

    function upperCase(target: any, propertyKey: string) {// console.log(target, propertyKey); // { getName: [Function (anonymous)], sun: [Function (anonymous)] 	} namelet value = target[propertyKey]const getter = () => value;const setter = (newVal: string) => { value = newVal.toUpperCase(); };if (delete target[propertyKey]) {Object.defineProperty(target, propertyKey, {get: getter,set: setter,enumerable: true,configurable: true});}
    }
    // 如果装饰的是静态属性,target就是构造函数
    function staticPrototypeDecorator(target: any, propertyKey: string) {// console.log(target, propertyKey); // [Function: Person] { age: 18 } age
    }
    function noEnumerable(target: any, propertyKey: string, descriptor: PropertyDescriptor) {descriptor.enumerable = false; // 不可枚举
    }
    function toNumber(target: any, propertyKey, descriptor: PropertyDescriptor) {let oldMethod = descriptor.value;descriptor.value = function (...args: any[]) {console.log(args) // ['1', '2', '3', '4']args = args.map(item => parseFloat(item));return oldMethod.apply(this, args)}
    }
    class Person {@upperCasename: string = 'jerry';   // 实例属性@staticPrototypeDecoratorstatic age: number = 18;  // 静态属性@noEnumerablegetName() { console.log(this.name); }; // 实例方法@toNumbersum(...args: any[]) { // 实例方法console.log(args) // [1, 2, 3, 4]return args.reduce((prev: number, next: number) => prev + next, 0);}
    }
    let p = new Person();
    console.log(p.name); // JERRY
    console.log(p.sum('1', '2', '3', '4')); // 10
    
  5. 参数装饰器
    参数装饰器声明在一个参数声明之前(紧靠着参数声明)。 参数装饰器应用于类构造函数或方法声明。 参数装饰器不能用在声明文件(.d.ts),重载或其它外部上下文里。

    参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:
    1)对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
    2)成员的名字。
    3)参数在函数参数列表中的索引。
    注意  参数装饰器只能用来监视一个方法的参数是否被传入。

    // target:静态属性指构造函数,实例属性、实例方法值构造函数的原型// methodName:方法的名称// paramIndex:参数的索引function addAge(target: any, methodName: string, paramIndex: number) {// console.log(target, methodName, paramIndex); // { login: [Function (anonymous)] } login 1target.age1 = 18;}class Person {age1: number;login(username: string, @addAge password: string) {console.log(this.age1, username, password); // 18 admin admin123}}let p = new Person();p.login('admin', 'admin123'); // 18 'admin' 'admin123'
    
  6. 装饰器执行顺序
    • 1)class装饰器最后执行,后写的类装饰器先执行
    • 2)方法和参数中的装饰器,参数装饰器先执行,再执行方法装饰器
    • 3)方法和属性装饰器,谁在前面先执行谁
    • 4)先内后外 先上后下执行
    function ClassDecorator1() {return function (target) {console.log('ClassDecorator1');}
    }
    function ClassDecorator2() {return function (target) {console.log('ClassDecorator2');}
    }
    function PropertyDecorator(name: string) {return function (target, propertyName) {console.log('PropertyDecorator', propertyName, name);}
    }
    function MethodDecorator() {return function (target, propertyName) {console.log('MethodDecorator', propertyName);}
    }
    function ParameterDecorator() {return function (target, propertyName, index) {console.log('ParameterDecorator', propertyName, index);}
    }
    @ClassDecorator1()
    @ClassDecorator2()
    class Person {@PropertyDecorator('name')name: string = '';@PropertyDecorator('age')age: number = 9;@MethodDecorator()hello(@ParameterDecorator() hello: string, @ParameterDecorator() word: string) {}
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/744315.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【智能算法】蜻蜓算法(DA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.代码实现4.参考文献 1.背景 2016年&#xff0c;Mirjalili受到蜻蜓静态和动态集群行为启发&#xff0c;提出了蜻蜓算法(Dragonfly algorithm, DA)。 2.算法原理 2.1算法思想 蜻蜓集群有两种行为目的&#xff1a;狩猎&#xf…

高端新颖知识竞赛活动方案

设计一场高端新颖知识竞赛活动&#xff0c;需要组织者用心策划&#xff0c;精心准备。下面这场竞赛设计新颖&#xff0c;可供大家组织竞赛活动时参考 。本竞赛分为“万箭齐发”、“城旗易主”、“群雄逐鹿”、“追风逐电”&#xff0c;四个环节&#xff0c;所有环节必须在主持人…

【CesiumJS-5】绘制动态路线实现飞行航线、汽车轨迹、路径漫游等

实现效果 前言 Cesium中&#xff0c;动态路线绘制的核心是借助CZML格式&#xff0c;CZML是一种用来描述动态场景的JSON数组,可以用来描述点、线、多边形、体、模型及其他图元,同时定义它们是怎样随时间变化的&#xff1b; CZML主要做三件事&#xff1a; 1.添加模型信息 2.添加…

如何在Linux系统部署APITable容器并实现无公网IP远程管理本地数据

文章目录 前言1. 部署APITable2. cpolar的安装和注册3. 配置APITable公网访问地址4. 固定APITable公网地址 前言 vika维格表作为新一代数据生产力平台&#xff0c;是一款面向 API 的智能多维表格。它将复杂的可视化数据库、电子表格、实时在线协同、低代码开发技术四合为一&am…

C#,蛇梯问题(Snake and Ladder Problem)的算法与源代码

1 蛇梯问题 Snake and Ladder Problem 给定一个蛇梯板,找出从源单元格或第一个单元格到达目标单元格或最后一个单元格所需的最小掷骰次数。基本上,玩家可以完全控制掷骰子的结果,并希望找出到达最后一个单元格所需的最小掷骰次数。 如果玩家到达的牢房是梯子的底部,玩家…

金融防线升级:构筑数据安全的不可逾越之墙

近日&#xff0c;中国人民银行专栏《金融科技支持高质量发展》指出&#xff0c;应不断增强金融行业的网络安全和数据安全保障能力&#xff0c;坚持总体国家安全观&#xff0c;持续完善金融网络安全、数据安全制度体系&#xff0c;依法开展金融业关键信息基础设施安全保护工作&a…

mac删除带锁标识的app

一 、我们这里要删除FortiClient.app 带锁 常规方式删除不掉带锁的 app【如下图】 二、删除命令&#xff0c;依次执行即可。 /bin/ls -dleO /Applications/FortiClient.app sudo /usr/bin/chflags -R noschg /Applications/FortiClient.app /bin/ls -dleO /Applications/Forti…

Wireshark4.2.3 x64 Setup20240313

参考&#xff1a;【抓包工具】win 10 / win 11&#xff1a;WireShark 下载、安装、使用&#xff08;https://blog.csdn.net/qq_39720249/article/details/128157223&#xff09; 文章目录 下载安装12 license 许可证3 donations&#xff1a;捐款4 Choose Components&#xff1…

算法-图的存储,图的转置,拓扑排序

1.图的存储 图用来对关系建模&#xff0e;图是节点和边构成的集合&#xff0e;节点反映图的元素集合&#xff0c;边反映图的元素集合中元素间的关系&#xff0e; 上述是由五个节点&#xff0c;三条边构成的结构&#xff0e;我们可以用图对其建模&#xff0e; 对由节点&#x…

【Python】Leetcode 240. 搜索二维矩阵 II - 削减矩阵+递归,击败88%

描述 搜索二维矩阵 II 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。 该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。 每列的元素从上到下升序排列。 思路 确定左右及上下限&#xff0c;削减矩阵&#xff0c;递归。 注意判断四个端…

API数据能力开放架构 数据api接口

一.API数据接口可以从几个不同的角度来看待&#xff1a; 1. 技术角度&#xff1a;API数据接口是一种技术实现&#xff0c;通常是以HTTP协议或其他网络协议为基础&#xff0c;并采用特定的数据格式&#xff08;如JSON或XML&#xff09;来传递数据。 2. 业务角度&#xff1a;API…

前端input上传文件 使用new FormData()同时上传文件类型和JSON数据

失败案例 async uploadFileHandler(e) {if (e.target.files.length) {const files e.target.files[0];let formData new FormData();formData.append("file", files);formData.append("module", "knowledge");fileupload(formData).then(res …

JVM探究

JVM探究 请你谈谈你对JVM的理解&#xff1f;java -> class -> jvm java 8虚拟机和之前的变化更新OOM 内存溢出。栈溢出 StackOverFlowError > 怎么分析JVM的常用调优参数 &#xff1f; 扩大内存内存快照如何抓取&#xff0c;怎么分析Dump文件&#xff1f;知道吗&…

SAP 生产订单中计划成本问题简介

最近财务在月结的时候,发现有生产订单结算的时候有物料不存在计划成本,并且财务顾问反馈这个问题已经存在有一段时间了。通过对生产订单的成本分析按钮查看生产订单的成本 发现确实存在有物料不存在计划成本,但是生产订单存在实际成本。 1、首先想到的是无计划成本的原材料…

C语言易错知识点:scanf函数

scanf在C语言学习中比较常用&#xff0c;但因为其涉及屏幕缓冲区导致有的时候会调入陷阱&#xff0c;下面分享一下常见的需要注意的事项&#xff1a; 1.输入末尾带有回车\n 当我们输入数据后&#xff0c;最后按下回车时&#xff0c;屏幕缓冲区的末尾都会含有这个字符 scanf的…

leetcode110.平衡二叉树

之前没有通过的样例 return语句只写了一个 return abs(l-r)<1缺少了 isBalanced(root->left)&&isBalanced(root->right);补上就好了 class Solution { public:bool isBalanced(TreeNode* root) {if(!root){return true;}int lgetHeight(root->left);i…

(学习日记)2024.03.11:UCOSIII第十三节:使用优先级的流程 (持续更新)

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

vulntarget-k - 内网渗透

标签 xxl-job rce Spring-Cloud-CVE-2022-22947 nacos auth bypass iox 靶机难度比较简单&#xff0c;都是用用 exp 就好了 拓扑图 网卡设置 首先需要使用虚拟网络编辑器&#xff0c;增加 VMnet1、VMnet2、VMnet3 对三张网卡设置子网 IP VMnet1 192.168.100.0 VMnet2 1…

EM算法详解

EM(Expectation-Maximum)算法也称期望最大化算法,曾入选“数据挖掘十大算法”中,可见EM算法在机器学习、数据挖掘中的影响力。EM算法是最常见的隐变量估计方法,在机器学习中有极为广泛的用途,例如常被用来学习高斯混合模型(Gaussian mixture model,简称GMM)的参数;隐…

SIP-7043 20W SIP网络有源吸顶喇叭

SIP-7043 20W SIP网络有源吸顶喇叭 一、描述 18123651365微信 声卡喊话IP喇叭&#xff0c;IP网络吸顶天花喇叭 SIP-7043VP是我司的一款SIP网络有源吸顶喇叭&#xff0c;具有10/100M以太网接口&#xff0c;内置有一个高品质扬声器&#xff0c;将网络音源通过自带的功放和喇叭…