TypeScript类型级别和值级别示例详解

目录
  • 对值级别编程类型级别编程区分
  • 类型级编程
  • 挑战是如何工作的
  • 挑战

对值级别编程类型级别编程区分

首先,让我们对值级别编程和类型级别编程进行重要区分。

  • 值级别编程让我们编写将在生产中运行的代码即运行期,并为我们的用户提供有用的东西。
  • 类型级别编程帮助我们确保代码在发布之前即编译期不包含错误,在运行期会被完全删除

JavaScript没有类型,所以所有JavaScript都是值级别的代码:

// A simple Javascript function:
function sum(a, b) {
  return a + b;
}

TypeScript允许我们将类型注释添加到JavaScript中,并确保我们编写的sum函数永远不会用数字以外的任何东西调用:

// Using type annotations:
function sum(a: number, b: number): number {
  return a + b;
}

但TypeScript的类型系统远比这强大得多。我们编写的真实代码有时需要是通用的,并接受我们事先不知道的类型。

在这种情况下,我们可以在尖括号<A,B,…>中定义类型参数然后,我们可以将类型参数传递给一个类型级函数,该函数根据输入类型计算输出类型:

// Using type level programming:
function genericFunction<A, B>(a: A, b: B): DoSomething<A, B> {
  return doSomething(a, b);
}

这就是类型级编程!DoSomething<A,B> 是一种用特殊编程语言编写的类型级函数,它与我们用于值的语言不同,但同样强大。让我们将这种语言称为类型级TypeScript。

// This is a type-level function:
type DoSomething<A, B> = ...
// This is a value-level function:
const doSomething = (a, b) => ...

类型级编程

类型级TypeScript是一种最小的纯函数语言。

在类型级别,函数被称为泛型类型:它们接受一个或多个类型参数并返回单个输出类型。下面是一个函数的简单示例,该函数使用两个类型参数并将它们包装在元组中:

type SomeFunction<A, B> = [A, B];
/*                ----    ------
                   ^         \
                  type        return type
               parameters
     \-------------------------/
                 ^
              Generic
*/

类型级别的TypeScript没有很多功能。毕竟,它是专门为你的代码做类型约束的!也就是说,它确实有足够的特性(几乎)图灵完备,这意味着你可以用它解决任意复杂的问题。

  • 代码分支:根据条件执行不同的代码路径(相当于值级别if/else关键字)。
  • 变量赋值:声明一个变量并在表达式中使用它(相当于值级别var/let关键字)。
  • 函数:可重复使用的逻辑单位,如我们在前面的示例中看到的。
  • 循环:通常通过递归。
  • 相等检查:==但适用于类型!
  • 还有更多!

这是我们将在接下来的章节中学习的语言类型的简要概述。现在,让我们开始第一次挑战吧!

挑战是如何工作的

在每一章结束时,你将有一些挑战需要解决,以将你的新技能付诸实践。它们看起来像这样:

namespace challenge {
  // 1. implement a generic to get the union
  // of all keys in an object type.
  type GetAllKeys<Obj> = TODO;
  type res1 = GetAllKeys<{ a: number }>;
  type test1 = Expect<Equal<res1, "a">>;
}
  • namespace 是一个鲜为人知的TypeScript功能,它可以让我们在专用范围内隔离每个挑战。
  • TODO 是占位符。这是您需要更换的!
  • res1=。。。 是泛型为某些输入类型返回的类型。您可以用鼠标将其悬停以检查其当前
  • type test1=Expect<Equal<res1,…>> 是类型级单元测试。用于判断TODO部分的代码是否正确

在此之前你要先定义好Expect和Equal

type Expect<T extends true> = T;
type Equal<X, Y> = (<T>() => T extends { [k in keyof X]: X[k]; } ? 1 : 2) extends <T>() => T extends { [k in keyof Y]: Y[k]; } ? 1 : 2 ? true : false;

挑战

准备好迎接你的第一个挑战了吗?出发:

/**
 * 1. The `identity` function takes a value of any type
 *    and returns it. Make it generic!
 */
namespace genericFunction {
  function identity(a: TODO): TODO {
    return a;
  }
  let input1 = 10;
  let res1 = identity(input1);
  type test1 = Expect<Equal<typeof res1, number>>;
  let input2 = "Hello";
  let res2 = identity(input2);
  type test2 = Expect<Equal<typeof res2, string>>;
}
/**
 * 2. `safeHead` takes an array, a default value
      and returns the first element of the array
      if it isn't empty. Make it generic!
 */
namespace safeHead {
  function safeHead(array: TODO[], defaultValue: TODO): TODO {
    return array[0] ?? defaultValue;
  }
  let input1 = [1, 2, 3];
  let res1 = safeHead(input1, 0);
  type test1 = Expect<Equal<typeof res1, number>>;
  let input2 = ["Hello", "Hola", "Bonjour"];
  let res2 = safeHead(input2, "Hi");
  type test2 = Expect<Equal<typeof res2, string>>;
}
/**
 * 3. `map` transforms all values in an array to a value of
 *    different type. Make it generic!
 */
namespace map {
  function map(array: TODO[], fn: (value: TODO) => TODO): TODO[] {
    return array.map(fn);
  }
  let input1 = [1, 2, 3];
  let res1 = map(input1, value => value.toString());
  type test1 = Expect<Equal<typeof res1, string[]>>;
  let input2 = ["Hello", "Hola", "Bonjour"];
  let res2 = map(input2, str => str.length);
  type test2 = Expect<Equal<typeof res2, number[]>>;
}
/**
 * 4. `pipe2` takes a value and pipes it into 2 functions
 *    sequentially. For example, `pipe2(x, f1, f2)` will
 *    result in `f2(f1(x))`. Make it generic!
 *
 */
namespace pipe2 {
  function pipe2(
    x: TODO,
    f1: (value: TODO) => TODO,
    f2: (value: TODO) => TODO
  ): TODO {
    return f2(f1(x));
  }
  let res1 = pipe2(
    [1, 2, 3],
    arr => arr.length,
    length => `length: ${length}`
  );
  type test1 = Expect<Equal<typeof res1, string>>;
  let res2 = pipe2(
    { name: 'Alice' },
    user => user.name,
    name => name.length > 5
  );
  type test2 = Expect<Equal<typeof res2, boolean>>;
}

以上就是TypeScript类型级别和值级别示例详解的详细内容,更多关于TypeScript类型级别值级别的资料请关注我们其它相关文章!

(0)

相关推荐

  • TypeScript 类型级别示例介绍

    介绍 这是一门在线课程,旨在将您的TypeScript技能从中级提升到高级.它将使你深入了解类型系统的基本原理,并指导你完成其高级功能.在这里,你会找到成为TypeScript专家所需的一切-不仅有深入的内容,还有练习新技能的有趣挑战,就像这里的这个. /** * Try assigning "World" to `type Hello`! */ type Hello = "..."; // Type-level unit tests! // If the next

  • TypeScript实现十大排序算法之冒泡排序示例详解

    目录 一. 冒泡排序的定义 二. 冒泡排序的流程 三. 冒泡排序的图解 四. 冒泡排序的代码 五. 冒泡排序的时间复杂度 六. 冒泡排序的总结 一. 冒泡排序的定义 冒泡排序是一种简单的排序方法. 基本思路是通过两两比较相邻的元素并交换它们的位置,从而使整个序列按照顺序排列. 该算法一趟排序后,最大值总是会移到数组最后面,那么接下来就不用再考虑这个最大值. 一直重复这样的操作,最终就可以得到排序完成的数组. 这种算法是稳定的,即相等元素的相对位置不会发生变化. 而且在最坏情况下,时间复杂度为O(

  • TypeScript十大排序算法插入排序实现示例详解

    目录 一. 插入排序的定义 二. 插入排序的流程 三. 插入排序的图解 四. 插入排序的代码 五. 插入排序的时间复杂度 六. 插入排序的总结 一. 插入排序的定义 插入排序就像是你打扑克牌,你从牌堆顶取一张牌,找到合适的位置插入到已有牌的顺序中,并不断重复这一步骤直到所有的牌都被 插入到合适的位置,最终使得整副牌有序. 与打牌类似,插入排序(Insertion sort)的实现方法是: 首先假设第一个数据是已经排好序的,接着取出下一个数据,在已经排好序的数据中从后往前扫描,找到比它小的数的位置

  • Manipulation-TypeScript DOM操作示例解析

    目录 DOM Manipulation 对 HTMLElement 类型的探索 基础案例 Document 接口 Document.getElementById Document.createElement Node 接口 Node.appendChild NodeList 接口 与 NodeListOf 接口 children 和 childNodes 的区别 querySelector 和 querySelectorAll 方法 DOM Manipulation 对 HTMLElement

  • TypeScript新语法之infer extends示例详解

    目录 正文 模式匹配 提取枚举的值的类型 总结 正文 我们知道,TypeScript 支持 infer 来提取类型的一部分,通过模式匹配的方式. 模式匹配 比如元组类型提取最后一个元素的类型: type Last<Arr extends unknown[]> = Arr extends [...infer rest,infer Ele] ? Ele : never; 比如函数提取返回值类型: type GetReturnType<Func extends Function> = F

  • Blazor实现组件嵌套传递值的示例详解

    实现创建一个Blazor Server空的应用程序 创建一个Tab.razor 并且添加以下代码 <div> @Title </div> @code { [CascadingParameter] public string? Title { get; set; } } 修改Index.razor组件代码 @page "/" <CascadingValue Value="Title"> <Tab/> </Casca

  • 使用TypeScript实现一个类型安全的EventBus示例详解

    目录 前言 准备工作 目标 思路 具体实现 全部代码 后记 前言 随着vue3的发布,TypeScript在国内越来越流行,学习TypeScript也随即变成了大势所趋.本文就通过实现一个类型安全的EventBus来练习TypeScript,希望对小伙伴们有所帮助. 准备工作 生成一个TypeScript的基础架子: // 创建目录 mkdir ts-event-bus && cd ts-event-bus // 初始化工程 yarn init -y // 安装typescript yar

  • TypeScript类型级别和值级别示例详解

    目录 对值级别编程类型级别编程区分 类型级编程 挑战是如何工作的 挑战 对值级别编程类型级别编程区分 首先,让我们对值级别编程和类型级别编程进行重要区分. 值级别编程让我们编写将在生产中运行的代码即运行期,并为我们的用户提供有用的东西. 类型级别编程帮助我们确保代码在发布之前即编译期不包含错误,在运行期会被完全删除 JavaScript没有类型,所以所有JavaScript都是值级别的代码: // A simple Javascript function: function sum(a, b)

  • TypeScript类型断言VS类型守卫示例详解

    目录 类型断言 类型守卫 使用 in 关键字 使用 instanceof 关键字 使用 typeof 关键字 自定义类型守卫 总结 类型断言 类型断言有两种写法,分别为value as Type和<Type>value,它让 TypeScript 编译器将 value 当作 Type 类型.类型断言是一个编译时特性,不进行类型转换,因此不会影响变量在运行时的数据类型.如果某变量是 any 类型,但现在你知道它确切的数据类型,使用类型断言能让 IDE 有代码提示的能力,也能让 TypeScrip

  • TypeScript 泛型推断实现示例详解

    目录 前言 基础类型准备 最终使用的方式 基于Interface的实现 (失败了) 所有内容都基于type 实现 完整Demo 结束语 前言 最近做东西都在用ts,有时候写比较复杂的功能,如果不熟悉,类型写起来还是挺麻烦的.有这样一个功能,在这里,我们就不以我们现有的业务来举例了,我们还是已Animal举例,来说明场景.通过一个工厂来创建不同的动物实例.在这里我们借助泛型来实现类型的约束和动态推到指定类型. 基础类型准备 用一个枚举来定义Animal的类型 enum EAnimalType {

  • ts 类型体操 Chainable Options 可链式选项示例详解

    目录 问题 答案 传参 option部分 get 问题 在JavaScript我们通常会使用到可串联(Chainable/Pipline)的函数构造一个对象,但是在Typescript中,你能合理地给它赋上类型吗? 题目是: 可以使用任何你喜欢的方式实现这个类型 - interface, type, 或者 class 都行.你需要提供两个函数option(key, value) 和 get() 在 option 中你需要使用提供的key和value来扩展当前的对象类型,通过 get()获取最终结

  • JS中原始值和引用值的储存方式示例详解

    在ECMAscript中,变量可以存放两种类型的值,即原始值和引用值 原始值指的是代表原始数据类型的值,也叫基本数据类型,包括:Number.Stirng.Boolean.Null.Underfined 引用值指的是复合数据类型的值,包括:Object.Function.Array.Date.RegExp 根据数据类型不同,有的变量储存在栈中,有的储存在堆中.具体区别如下: 原始变量及他们的值储存在栈中,当把一个原始变量传递给另一个原始变量时,是把一个栈房间的东西复制到另一个栈房间,且这两个原始

  • Go语言基础类型及常量用法示例详解

    目录 基础类型 概述 按类别有以下几种数据类型 数值类型 派生类型 变量 概述 单个变量声明 多个变量声明 基础类型 概述 在 Go 编程语言中,数据类型用于声明函数和变量.数据类型的出现时为了把数据分成所需要用大数据的时候才需要申请大内存,这样可以充分的列用内存. 按类别有以下几种数据类型 数值类型 布尔型 bool:布尔型的值只可以是常量 true 或者 false,默认值为 false. 字符串类型 string:编码统一为 UTF-8 编码标识 Unicode 文本,默认值为空字符串.

  • TS 类型兼容教程示例详解

    目录 类型兼容 简单类型兼容 普通对象兼容 函数兼容 参数数量不一致 参数类型不一致 返回不同 类型兼容 因为JS语言不慎过于领过, 真实开发场景中往往无法做到严格一致的类型约束,此时TS就不得不做类型兼容 顶类型:unknown -- 任何类型都可以赋值给unknown 底类型:never -- never兼容任何类型(可以赋值给任何类型) any: 其实不是一个类型,它是一个错误关闭器,用了any就等同于放弃了类型约束 简单类型兼容 子集可以赋值给父级 type name = string

随机推荐