热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使用typescript推导已有变量的盲盒类型详情_javascript技巧

这篇文章主要介绍了使用typescript推导已有变量的盲盒类型详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要

迁移盲盒

当我们从Javascript一键转换Typescript的时候,any便是最省事的做法,对于维护并不友好(虽然能跑就行),同时每个变量对于我们来说都是盲盒,它到底是什么类型?

类型推导

基础类型的推导

基础数据类型的类型推导还是挺简单的

let a = 1;
type A = typeof a; // number;
let b = '2'
type B = typeof b; // string;
let c;
type C = typeof c; // undefined;

一个typeof就可以把原始值类型推导出来了!

我们整理下基础数据类型有哪些?

// 没错,7种数据类型
type Base = string | number | boolean | null | undefined | symbol | bigint;

对象的推导

这里我们来实现下普通对象如何推导

let obj = { a: 1, b: '2', c: true, d: null };
type Obj = typeof obj;
// { a: number; b: string; c: boolean; d: null; }

没错,也是这么简单!

数组的推导

为什么上面的对象除开数组呢?因为数组在typescript比较特殊,既可以用元祖来声明,又可以用数组来声明

也可以说数组包含了元祖

type isCOntain= [1, 2] extends Array ? true : false; // true

尝试继续通过typeof来实现推导

let arr = [1, '2', null, false];
type Arr = typeof arr; // (string | number | boolean | null)[] ???
type Arr1 = typeof arr[0] // string | number | boolean | null ???

好吧,得到的竟然是联合类型数组,是不是和我们预期的不一样?

我们定义一个数组,却没有声明类型,对于数组来说,默认就是 Array,由于不断填充了 number,string,null,boolean的值,最后变成了 Array,而且是每一个元素都是联合类型

重新整理下,我们需要的是啥?

[1, '2', null, false] -> [number, string, null, boolean]

我们要的是元祖 [number, string, null, boolean],而不是数组 Array

整理下todo,我们需要的是:


  • 固定长度的数组类型

  • 每个元素都有独立的类型


let arr = [1, '2', null, false] as const;
type Arr = typeof arr; // readonly [1, '2', null, false]
type Arr1 = typeof arr[0] // 1

第一个 todo 实现了,第二个有点像,但又不对,我们要的是数据类型,而不是某个具体的值

实现一个转换类型的泛型

我们要的是 1 转 number, 'A' 转 string,只需要列出所有的基础类型做转换就可以了

type GetType = T extends string ? string :
T extends number ? number :
T extends boolean? boolean :
T extends null ? null :
T extends undefined ? undefined :
T extends symbol ? symbol :
T extends bigint ? bigint : T;
type Arr1 = typeof arr[0] // number
type Arr2 = typeof arr[1] // string

那再遍历一次元祖就可以实现整个数组的类型转换了

type TransArr,
R extends unknown[] = []> = {
'loop': TransArr [...R,
T extends Base ?
GetType: T[R['length']]
]
>,
'result': R,
}[T['length'] extends R['length'] ? 'result': 'loop'];
let arr = [1, '2', null, false] as const;
type Arr = typeof arr;
type ArrType = TransArr; // [number, string, null, boolean]

函数的推导

函数的推导其实没有必要,为什么这么说,函数参数和值类型不可控,除个别操作符或者明确类型

如 (a, b) => a * b ,返回值一定是number

如 (a) => Promise.resolve(a),返回值一定是Promise

let fn1 = (a, b) => a * b;
type Fn1 = typeof fn1; // (a: any, b: any) => number
function fn2(a) {
return Promise.resolve(a);
}
type Fn2 = typeof fn2; // (a: any) => Promise

大多是函数经过typeof后得到的结果是

(a: any, b: any, ...) => any;

这个类型可以限定参数数量更多的函数

function fn3(a, b, c) {
return a + b + c;
}
function fn4(d) {
return d + 1;
}
type Fn3 = typeof fn3; // (a: any, b: any, c: any) => any
type Fn4 = typeof fn4; // (d: any) => any
type F3_4 = Fn3 extends Fn4 ? true : false; // false
type F4_3 = Fn4 extends Fn3 ? true : false; // true

也就是说,参数多的函数总是包含了参数少的函数

根据上面的判断,我们可以通过这个来实现函数的判断

type isFunc = (() => any) extends T ? true : false;

完善推导



  • 基础类型直接返回类型

  • 数组用TransArr泛型转一次

  • 函数直接返回typeof的值

  • 遍历对象则用keyof实现


type Trans = T extends Base
? GetType : T extends Array
? TransArr : isFunc extends true
? T : {
[key in keyof T]: T[key] extends Base
? GetType : T[key] extends Array
? TransArr : Trans;
};

测试


let a1 = 1;
type test1 = Trans; // number
let a2 = '2';
type test2 = Trans; // string
let a3 = [1, '2', true, '3', 4] as const;
type test3 = TransArr;
// [number, string, boolean, string, number]
let a4 = {
a: 1,
b: true,
c: {
a: 1,
b: [1, '2']
},
d: [true, null]
} as const;
type test4 = Trans;
// {
// readonly a: number;
// readonly b: boolean;
// readonly c: {
// readonly a: number;
// readonly b: readonly [number, string];
// };
// readonly d: readonly [boolean, null];
// }
let a5 = {
a: [
{
b: [
{ c: 1 }
]
}
]
} as const;
type test5 = Trans;
// {
// readonly a: readonly [{
// readonly b: readonly [{
// readonly c: number;
// }];
// }];
// }
let a6 = (a, b, c) => a + b + c;
type test6 = Trans;
// (a: any, b: any, c: any) => any
let a7 = [
function fn() {
return 1;
},
(a, b) => a * b,
(a) => Promise.resolve(a)
] as const;
type test7 = TransArr;
// [() => number, (a: any, b: any) => number, (a: any) => Promise]
let a8 = {
a: 1,
b: [true, null],
c: [() => void, (a, b) => a],
d: {
e: [
(a, b) => null,
{
f: [1]
}
]
}
} as const;
type test8 = Trans;
// {
// readonly a: number;
// readonly b: readonly [boolean, null];
// readonly c: readonly [() => undefined, (a: any, b: any) => any];
// readonly d: {
// readonly e: readonly [(a: any, b: any) => null, {
// readonly f: readonly [number];
// }];
// };
// }


推荐阅读
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • Spring Boot 中配置全局文件上传路径并实现文件上传功能
    本文介绍如何在 Spring Boot 项目中配置全局文件上传路径,并通过读取配置项实现文件上传功能。通过这种方式,可以更好地管理和维护文件路径。 ... [详细]
  • Ihavetwomethodsofgeneratingmdistinctrandomnumbersintherange[0..n-1]我有两种方法在范围[0.n-1]中生 ... [详细]
  • javascript分页类支持页码格式
    前端时间因为项目需要,要对一个产品下所有的附属图片进行分页显示,没考虑ajax一张张请求,所以干脆一次性全部把图片out,然 ... [详细]
  • 重要知识点有:函数参数默许值、盈余参数、扩大运算符、new.target属性、块级函数、箭头函数以及尾挪用优化《深切明白ES6》笔记目次函数的默许参数在ES5中,我们给函数传参数, ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • 检查在所有可能的“?”替换中,给定的二进制字符串中是否出现子字符串“10”带 1 或 0 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统
    技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统 ... [详细]
  • 本文详细解析了客户端与服务器之间的交互过程,重点介绍了Socket通信机制。IP地址由32位的4个8位二进制数组成,分为网络地址和主机地址两部分。通过使用 `ipconfig /all` 命令,用户可以查看详细的IP配置信息。此外,文章还介绍了如何使用 `ping` 命令测试网络连通性,例如 `ping 127.0.0.1` 可以检测本机网络是否正常。这些技术细节对于理解网络通信的基本原理具有重要意义。 ... [详细]
  • 2.2 组件间父子通信机制详解
    2.2 组件间父子通信机制详解 ... [详细]
  • Flutter 2.* 路由管理详解
    本文详细介绍了 Flutter 2.* 中的路由管理机制,包括路由的基本概念、MaterialPageRoute 的使用、Navigator 的操作方法、路由传值、命名路由及其注册、路由钩子等。 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • 本文介绍了如何利用 `matplotlib` 库中的 `FuncAnimation` 类将 Python 中的动态图像保存为视频文件。通过详细解释 `FuncAnimation` 类的参数和方法,文章提供了多种实用技巧,帮助用户高效地生成高质量的动态图像视频。此外,还探讨了不同视频编码器的选择及其对输出文件质量的影响,为读者提供了全面的技术指导。 ... [详细]
  • 在JavaWeb开发中,文件上传是一个常见的需求。无论是通过表单还是其他方式上传文件,都必须使用POST请求。前端部分通常采用HTML表单来实现文件选择和提交功能。后端则利用Apache Commons FileUpload库来处理上传的文件,该库提供了强大的文件解析和存储能力,能够高效地处理各种文件类型。此外,为了提高系统的安全性和稳定性,还需要对上传文件的大小、格式等进行严格的校验和限制。 ... [详细]
author-avatar
余温
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有