推断精确值打字稿

2023-12-07

如何让打字稿从传递的参数推断返回值

const toggle = <T, O extends [T,T]>(initialValue: T, options: O) => {
      return [initialValue, options];
    }

const [value, opts] = toggle("light", ["light", "dark"]);

值的类型是string,我需要它是"light" | "dark"


从概念上讲,您的toggle()函数可以简单地输入为:

const toggle = <T, U>(initialValue: T | U, options: readonly [T, U]) => {
  return [initialValue, options] as const;
}

这里有两个generic类型参数T and U对应于第一和第二成员options tuple(标记为readonly tuple这实际上是less比普通读写元组有限制)。以及类型initialValue is the union of T and U.

这将捕获您输入类型中的主要错误:

toggle(3, ["abc", true]); // error!
// --> ~ Argument of type '3' is not assignable to parameter of type 'string | boolean'.
// toggle<string, boolean>(...)

Here T被推断为string, and U被推断为boolean,和输入3不匹配string | boolean.

但不幸的是,由于 TypeScript 中类型推断的工作方式,它不会将以下内容检测为错误:

toggle("oops", ["light", "dark"]); // no error
// toggle<string, string>(...)

毕竟,T is string and U is string, and "oops"也是一个string。但你希望编译器处理"light", "dark", and "oops", 作为字符串文字类型, 以便"light"属于类型"light",这不兼容"oops".


TypeScript 编译器使用启发式方法来推断值的类型。当它看到值时{name: "jon"},它倾向于推断{name: string}, 假如说"jon"只是一个属性的初始化器,它可能需要任何string价值。这往往是人们想要的。但有时并非如此。有时人们希望将整个值视为不可变的尽可能,因此类型应该是specific尽可能。

在这些情况下,您可以使用const断言告诉编译器:

let v = "light"; // string
let w = "light" as const; // "light"
let x = { name: 'jon' }; // { name: string }
let y = { name: 'jon' } as const; //  { readonly name: "jon" }

如果我们使用const调用时对两个输入进行断言toggle(),事情会突然按照你想要的方式进行:

toggle("oops" as const, ["light", "dark"] as const); // error!
// --> ~~~~~~~~~~~~~~~ 
// Argument of type '"oops"' is not assignable to parameter of type '"light" | "dark"

const [v1, o1] = toggle("light" as const, ["light", "dark"] as const);
// v1: "light" | "dark"
const [v2, o2] = toggle({ a: 456 } as const, [{ a: 456 }, { b: 789 }] as const)
// v2: { readonly a: 456 } | { readonly b: 789 }
const [v3, o3] = toggle(true as const, [true, false] as const);
// v3: boolean
const [v4, o4] = toggle({ name: "jon" } as const, [{ name: "jon" }, { name: "amy" }] as const);
// v4: { readonly name: "jon" } | { readonly name: "amy" }

这很好,但它依赖于caller of toggle()用一个const断言。


如果你能的话那就太好了实施 toggle()以这样的方式,通用推论T and U类型参数可以是“const-asserted”,这样调用者就不必写as const如果他们将文字传递给toggle().

不幸的是,没有简单的方法可以做到这一点。不久前我提交了微软/TypeScript#30680请求对此的支持,但尚不清楚何时或是否会实施。

目前,您可以使用一些技巧来获得类似的行为,但它们并不漂亮。如果您有泛型类型参数X extends string,它会倾向于推断字符串文字类型X. And X extends number将对数字文字执行相同的操作。所以X extends string | number | boolean将推断字符串文字、数字文字和布尔文字。但如果你希望在嵌套级别推断这些,你需要类似的东西X extends string | number | boolean | {[k: string]: X}。如果您想推断元组类型而不是无序数组,则您的推断域中还需要一些元组类型,所以也许X extends string | number | boolean | [] | {[k: string]: X}。而且您不想禁止其他类型,因此您需要在其中包含其他内容,例如null and object。理想情况下你想包括the unknown type因为它允许一切,但这会抛弃所有暗示。所以你需要定义一个Narrowable类型是like unknown除非它可以用来缩小到文字。

这给了你这个:

type Narrowable = string | number | boolean | symbol | bigint
  | null | undefined | object | {} | [] | void;

const toggle = <
  T extends Narrowable | { [k: string]: T },
  U extends Narrowable | { [k: string]: U }
>(initialValue: T | U, options: [T, U]) => {
  return [initialValue, options] as const;
}

让我们看看它是否有效:

toggle("oops", ["light", "dark"]); // error

const [v1, o1] = toggle("light", ["light", "dark"]);
// v1: "light" | "dark"

const [v2, o2] = toggle({ a: 456 }, [{ a: 456 }, { b: 789 }])
// v2: { a: 456 } | { b: 789 }

const [v3, o3] = toggle(true, [true, false]);
// v3: boolean

const [v4, o4] = toggle({ name: "jon" }, [{ name: "jon" }, { name: "amy" }]);
// v4: { name: "jon" } | { name: "amy" }

看起来不错。当您使用时,它的行为与原始版本非常相似as const,除了它不是推断readonly对象的属性...您可能一开始并不真正关心。

Playground 代码链接

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

推断精确值打字稿 的相关文章

随机推荐

  • 将 None 转换为空字符串的最惯用方法? [关闭]

    Closed 这个问题是基于意见的 目前不接受答案 执行以下操作最惯用的方法是什么 def xstr s if s is None return else return s s xstr a xstr b update 我采纳了 Trypt
  • 在 PHP 中用如此命名的变量替换括号内的文本

    我想替换方括号中的所有字符串 以及从名为该字符串的数组中随机选择的项目 它非常类似于这个问题 但有一点不同 因为我想用名为该数组的字符串替换不同括号的内容 一个例子应该会让这一点更清楚一些 所以说我已经拿到了字符串 This is a ve
  • 获取同一html中隐藏字段的值? [复制]

    这个问题在这里已经有答案了 我想得到的值hidden我的 html 中的字段
  • 如何从注入同一程序的另一个 .dll 调用函数?

    我的问题确实在上面 但是我将在下面提供更多信息 我有一个程序 它首先获取我的 假 d3d9 dll 然后将该DLL加载到我正在进行逆向工程的游戏中 一段时间后 dll 被加载 以及所有其他游戏依赖项 我想注入我的 DLL 它将完成逆向工程的
  • 代表的目的[重复]

    这个问题在这里已经有答案了 复制 事件和委托的区别及其各自的应用 代表的优势是什么 我在哪里使用代表 我想知道代表们的目的是什么 我没怎么用过它们 也想不出什么东西 在我的课程中 写到委托是所有符合其签名的方法的蓝图 此外 您可以向一个委托
  • 以不同的名称循环保存图像

    我在循环保存裁剪后的图像时遇到问题 我的代码 def run self image file print image file cap cv2 VideoCapture image file while cap isOpened ret f
  • 模板化成员函数 typedef 无法编译

    include
  • Sagepay Forms 加密方法 经典 ASP

    我有一个使用 Classic ASP 开发的定制购物车 我需要将其与 SagePay Forms 集成 我正在努力寻找任何记录的 ASP 代码示例 说明如何加密发送到 Sagepay 的订单数据 做过这件事的人能给我指出正确的方向吗 有一些
  • MongoDB中如何对2个字段的总和进行排序

    我有这个文件 id 59b804e1ee8a4071a5ea3fcc description description imagepath https example com type label downvotes 25 upvotes 1
  • 为 git-diff 输出中的空白着色

    关于代码格式 我是纯粹主义者 我经常删除不必要的空格 仅包含 ws 的行 行尾的 ws 等 我什至将 vim 设置为显示那种红色的线条 我的问题是 使用 git diff 我经常看到这样的东西 else else 即使我有 git diff
  • 我可以将 mono64 与 Visual Studio for Mac 结合使用吗?

    我是 Mono 开发 但不是 NET 开发 的新手 下载了 Visual Studio for Mac 并尝试从 Alea GPU 运行示例解决方案 它抱怨它需要 mono64 但我没有看到像在 Windows 中那样选择 64 位架构的选
  • C语言中的空函数指针是什么意思?

    假设我们有一个函数指针 void func0 void 其还定义为 void func0 void printf 0 n 但是 假设在某些时候我们尝试以某种方式访问 函数指针 那么当我进入代码时 如果 MS VS 调试器显示 func0 实
  • 取消格式化磁盘大小字符串

    有什么方法可以转换字符串吗1K to 1000 1M to 1000000等等 用一个 Bash 命令 我想避免成为第 100 万第 30 个人为此创建超过 10 行或超过 100 个字符的单行 hack 就像是iso2int 5MB Ed
  • Hibernate使用Oracle序列时不生成标识符

    我有以下映射 Entity SequenceGenerator name sacpSequenceGenerator sequenceName SACP SEQ public class Sacp private Integer id Id
  • 使用类成员函数作为回调?

    我需要将一个成员函数传递给第三方外部方法 box self intersection d mycallback The box self intersection d是第三方外部静态方法 我无法修改它 mycallback是我想将其传递到的
  • 在 IE7 中垂直居中块

    我试图在 IE7 中垂直居中一个块 如果可能的话 也可以在 IE6 中 让我弄清楚一件事 我不是垂直居中实际元素 而是元素内的文本 这是我的 CSS 和 HTML 适用于 IE8 及以上版本 但不适用于以下版本 a display tabl
  • 仅当第一个非空 android 时才关注第二个编辑文本

    目前我有两个编辑文本 假设我想对空编辑文本检查进行验证 运行时验证的更好方法是什么 我的代码是 final EditText ev1 EditText findViewById R id editText1 final EditText e
  • MapFragment Google Maps API v2 内存不足错误/内存泄漏

    在过去的几天里 我读了很多类似的帖子 但没有一个专门回答这个问题 在使用混合地图进行大约 2 分钟的激进地图平移和缩放后 我开始遇到 OOM 问题 该应用程序似乎还在崩溃后保留了内存 因为在第一次失败后达到 OOM 所需的时间更少 我将代码
  • 单实例并在系统托盘中通知

    我正在开发该应用程序 C 2 0 我已经在其中实现了单个实例 一切安好 如果我再次运行该应用程序 它会显示消息框 提示 实例已在运行 实际上我不想通过消息框显示消息 我想使用已运行实例的气球提示来显示此消息 它在系统托盘中有通知图标 我怎样
  • 推断精确值打字稿

    如何让打字稿从传递的参数推断返回值 const toggle