使用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<number> ? 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<string | number | boolean | null>,而且是每一个元素都是联合类型

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

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

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

整理下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> = 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<T extends Array<unknown>,
    R extends unknown[] = []> = {
        'loop': TransArr<T,
            [...R,
                T extends Base ?
                    GetType<T[R['length']]>: 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<Arr>; // [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<any>

大多是函数经过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<T> = (() => any) extends T ? true : false;

完善推导

  • 基础类型直接返回类型
  • 数组用TransArr泛型转一次
  • 函数直接返回typeof的值
  • 遍历对象则用keyof实现
type Trans<T> = T extends Base
    ? GetType<T> : T extends Array<unknown>
    ? TransArr<T> : isFunc<T> extends true
    ? T : {
        [key in keyof T]: T[key] extends Base
            ? GetType<T[key]> : T[key] extends Array<unknown>
            ? TransArr<T[key]> : Trans<T[key]>;
        };

测试

let a1 = 1;
type test1 = Trans<typeof a1>; // number
let a2 = '2';
type test2 = Trans<typeof a2>; // string
let a3 = [1, '2', true, '3', 4] as const;
type test3 = TransArr<typeof a3>;
// [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<typeof a4>;
// {
//     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<typeof a5>;
// {
//     readonly a: readonly [{
//         readonly b: readonly [{
//             readonly c: number;
//         }];
//     }];
// }
let a6 = (a, b, c) => a + b + c;
type test6 = Trans<typeof a6>;
// (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<typeof a7>;
// [() => number, (a: any, b: any) => number, (a: any) => Promise<any>]
let a8 = {
    a: 1,
    b: [true, null],
    c: [() => void, (a, b) => a],
    d: {
        e: [
            (a, b) => null,
            {
                f: [1]
            }
        ]
    }
} as const;
type test8 = Trans<typeof a8>;
// {
//     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];
//         }];
//     };
// }

到此这篇关于使用typescript推导已有变量的盲盒类型详情的文章就介绍到这了,更多相关typescript盲盒类型内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • TypeScript对于Duck类型和模块命名空间应用

    目录 一.TypeScript 鸭子类型 二.TypeScript 命名空间 三.TypeScript 模块 四.类型脚本声明文件 一.TypeScript 鸭子类型 Duck类型是一种动态类型和多态形式.在这种风格中,对象的有效语义不是通过从特定类继承或实现特定接口来确定的,而是通过“当前方法和属性的集合”来确定的. var object_name = { key1: "value1", // 标量 key2: "value", key3: function()

  • 使用typescript类型来实现快排详情

    目录 前言 元组快排 实现逻辑 实现数字的大小比较 实现 A 是否 小于或等于 B 实现 A 是否 大于或等于 B 实现Filter 优化Filter 重构数字的大小值比较 重构Filter 实现快排 测试快排 优化:负数 负数的判断 字符串转数字 获取负数的值 完善获取绝对值 重构数字的大小比较 重构快排 测试快排V2 前言 本文执行环境typescript,版本4.7.4 不使用typescript的计算能力,通过类型来实现快排 元组快排 能否将元组 [3, 1, 2, 4] 通过泛型转换成

  • TypeScript 类型编程之索引类型递归去掉可选修饰

    目录 前言 总结 前言 这两天东东遇到一个 TS 的问题,跑来问我. 问题是这样的: 这样一个 interface,想取出 userInfo 的类型来: interface Result{ data?: { userInfo?: { name: string; } } } 他是这样取的: type userInfo = Result['data']['userInfo']; 但是会报错: 说是 userInfo 不在这个联合类型上. 这很正常,因为可选索引的含义就是值和 undefined 的联

  • 使用typescript类型实现ThreeSum

    目录 前言 思路整理 实现TwoSum 实现减法 元祖中是否包含差值 递归元组 测试 实现ThreeSum 实现排序 实现ThreeSum 测试 前言 本文执行环境typescript,版本4.7.4 不使用typescript的计算能力,通过类型来实现ThreeSum 思路整理 实现ThreeSum之前我们先降低下难度,实现TwoSum,因为TwoSum可以作为ThreeSum的基础泛型 TwoSum需要准备什么呢? 递归元组,模拟for循环 减法,递归过程中求出差值 对每一项差值判断是否存在

  • TypeScript中命名空间与模块化详情

    目录 一.模块 二.命名空间 三.区别 一.模块 TypeScript 与ECMAScript 2015 一样,任何包含顶级 import 或者 export 的文件都被当成一个模块 相反地,如果一个文件不带有顶级的import或者export声明,那么它的内容被视为全局可见的 例如我们在在一个 TypeScript 工程下建立一个文件 1.ts,声明一个变量a,如下: const a = 1 然后在另一个文件同样声明一个变量a,这时候会出现错误信息 提示重复声明a变量,但是所处的空间是全局的

  • 你需要知道的TypeScript高级类型总结

    目录 1. 字面量类型 (1)字符串字面量类型 (2)数字字面量类型 (3)布尔字面量类型 (4)模板字面量类型 2. 联合类型 (1)基本使用 (2)限制 (3)可辨识联合类型 3. 交叉类型 (1)基本实用 (2)使用场景 4. 索引类型 (1)索引类型查询操作符 (2)索引访问操作符 (3)应用 5. 条件类型 (1)基本概念 (2)创建自定义条件类型 (3)条件类型的类型推断 6. 类型推断 (1)基础类型 (2)多类型联合 (3)上下文类型 7. 类型保护 (1)instanceof

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

    目录 迁移盲盒 类型推导 基础类型的推导 对象的推导 数组的推导 函数的推导 完善推导 测试 迁移盲盒 当我们从JavaScript一键转换Typescript的时候,any便是最省事的做法,对于维护并不友好(虽然能跑就行),同时每个变量对于我们来说都是盲盒,它到底是什么类型? 类型推导 基础类型的推导 基础数据类型的类型推导还是挺简单的 let a = 1; type A = typeof a; // number; let b = '2' type B = typeof b; // stri

  • 浅谈python中的变量默认是什么类型

    1.type(变量名),输出的结果就是变量的类型: 例如 >>> type(6) <type 'int'> 2.在Python里面变量在声明时,不需要指定变量的类型,变量的类型是动态指定的:>>> x=5 >>> type(x) <type 'int'> >>> x="wang" >>> type(x) <type 'str'> 3.也就是说变量的类型,根据给出

  • Sql学习第一天——SQL 将变量定义为Table类型(虚拟表)

    SQL 将变量定义为Table类型 在平时定义sql语句中的变量时通常我们定义的都是像char,varchar,nvarchar,int........,那如何让变量作为一个像虚拟表一样呢,其实很简单. 基本语法: 复制代码 代码如下: declare @t table(列名1 列的数据类型1 , 列名2 列的数据类型2 , ...............) insert into @t(列名1 ,列名2 ,...............) values (...............) [c

  • 判断一个变量是数组Array类型的方法

    在很多时候,我们都需要对一个变量进行数组类型的判断.JavaScript中如何判断一个变量是数组Array类型呢?我最近研究了一下,并分享给大家,希望能对大家有所帮助. JavaScript中检测对象的方法 1.typeof操作符 这种方法对于一些常用的类型来说那算是毫无压力,比如Function.String.Number.Undefined等,但是要是检测Array的对象就不起作用了. 复制代码 代码如下: alert(typeof null); // "object" alert

  • Python入门变量的定义及类型理解

    变量的定义 在程序中,有时我们需要对2个数据进行求和,那么该怎样做呢? 大家类比一下现实生活中,比如去超市买东西,往往咱们需要一个菜篮子,用来进行存储物品,等到所有的物品都购买完成后,在收银台进行结账即可. 如果在程序中,需要把2个数据,或者多个数据进行求和的话,那么就需要把这些数据先存储起来,然后把它们累加起来即可. 在Python中,存储一个数据,需要一个叫做 变量 的东西,如下示例: num1 = 100 #num1就是一个变量,就像一个小菜篮子 num2 = 87 #num2也是一个变量

  • TypeScript编写自动创建长度固定数组的类型工具详解

    目录 前言 代码 判断 List 的长度是否等于 Len 前言 在 TypeScript 中,当需要一个长度固定的数组时,通常会想到使用元组来进行表示,不过相对于数组而言,元组的每个元素的类型都不必是一致的. 如果现在需要一个长度为 30,元素类型为 string 的数组类型,其实就是一个元组,如果直接手写出来,那也太麻烦了,本文因此有感而发,编写了自动创建的类型工具. 代码 首先,不管三七二十一,先把这个类型工具给定义出来: type FixedArray = any 然后开始逐步分析,先从泛

  • C++编译期循环获取变量类型详情

    目录 一.问题 二.解决方案 1.定义类型 2.定义属性集 3. 获取类型索引 4. 编译期循环 总结 一.问题 假设现在有一些属性以及这些属性对应的数值类型,比如: "gender" --> char "age" --> int "height" --> float "IQ" ---> int "name" --> std::string "weight"

  • python的变量和简单数字类型详解

    目录 1. 变量 1.1 使用变量名时避免命名错误 2.字符串 2.1 修改字符串大小写的方法 2.2 合并字符串 2.3 使用制表符或换行符来添加空白 2.4 删除空白 2.5 使用字符串时需要避免语法错误 3. 数字类型 3.1 整数 3.2 浮点数 3.3 复数 3.4 使用函数str()避免类型错误 4 .注释 5 .python之禅 总结 1. 变量 每个变量都存储了一个值 在程序中可以随时修改变量,但Python将始终记录变量的最新值 message = "Hello Huang Z

  • TypeScript 映射类型详情

    目录 1.映射类型(Mapped Types) 2.映射修饰符(Mapping Modifiers) 3.通过 as 实现键名重新映射(Key Remapping via as) 4.深入探索(Further Exploration) 前言: TypeScript 的官方文档早已更新,但我能找到的中文文档都还停留在比较老的版本.所以对其中新增以及修订较多的一些章节进行了翻译整理. 本篇翻译整理自 TypeScript Handbook 中 「Mapped Types」 章节. 本文并不严格按照原

  • golang如何用type-switch判断interface变量的实际存储类型

    目录 用type-switch判断interface的实际存储类型 type-switch就刚好帮我解决了这个问题 golang任何类型interface{} example1 example2 用type-switch判断interface的实际存储类型 在go语言中,interface很好用,但是往往我们无法确定interface里存储的到底是什么类型,而且go又是强类型语言. type-switch就刚好帮我解决了这个问题 //例子 var s interface{} switch s.(

随机推荐