gpt4 book ai didi

TypeScript 没有正确检查对象属性

转载 作者:行者123 更新时间:2023-12-05 05:08:26 24 4
gpt4 key购买 nike

我有一个具有字符串和数字属性值的对象。当我想设置某些字段的值时,TypeScript 在使用 indexOf 方法进行检查时无法检查属性类型。

例子如下:

type TSomeObject = {
title: string,
description: string,
count: number
}

const someFunction = (myObj: TSomeObject, field: keyof TSomeObject, newValue: string): TSomeObject => {
if (field === 'title' || field === 'description') {
myObj[field] = newValue
}
return myObj
}

const otherFunction = (myObj: TSomeObject, field: keyof TSomeObject, newValue: string): TSomeObject =>{
const editableFields = ['title', 'description']
if (editableFields.indexOf(field) >= 0) {
// TypeScript error here
myObj[field] = newValue
}
return myObj
}

请参阅 TS playground 中的示例.

虽然 someFunction 工作得很好,但 otherFunction 失败并出现 TypeScript 错误:

Type 'string' is not assignable to type 'never'.

我知道它与类型为 numbercount 属性有关,但是嘿,我检查了我正在编辑哪个字段。我在这里做错了什么?

最佳答案

这里发生了一些事情。


一个是editableFields被推断为 string[] ,这对于您的目的来说太宽了。从 TS3.4 开始,您可以使用 const assertion为文字推断更窄的类型:

const editableFields = ['title', 'description'] as const;
// const editableFields: readonly ["title", "description"]

现在editableFields是一个元组,其成员已知为 "title""description" .


接下来,TypeScript 不会获取 arr.indexOf(x) >= 0 的结果成为type guard关于 x 的类型.所以即使测试是true , x 的类型未缩小到 typeof arr[number]自动地。这只是 TypeScript 的一个限制;并非所有可能的测试方法都可以被编译器检测到。幸运的是,TypeScript 使您能够制作 user-defined type guard functions .在这种情况下,您可能想做这样的事情:

declare function arrayContains<T extends U, U>(
haystack: ReadonlyArray<T>,
needle: U
): needle is T;

(我使用 ReadonlyArray 而不是 Array,因为它更通用。每个 Array 都是一个 ReadonlyArray,反之亦然。是的,这是一个奇怪的名字,因为 ReadonlyArray 可能不会被读取-only;也许 ReadonlyArray 应该是 DefinitelyReadableButNotNecessarilyWritableArrayArray 应该是 DefinitelyBothReadableAndWritableArray。也许不是。)

这是一个通用的用户定义类型保护,它接受 haystack某种类型的数组 Array<T> , 和一个 needle某种类型的UT 宽, 如果它返回 true , 编译器理解 needle is T . (它认为 false 意味着 needle不是 T ,即 might not always be desirable 。在你的情况下,因为 TU都将是字符串文字或它们的并集,这不是问题。)

如果我们有 arrayContains() 的实现,我们将测试更改为:

if (arrayContains(editableFields, field)) {
myObj[field] = newValue; // no error now
}

那么,让我们来实现 arrayContains() .


接下来是:TypeScript's standard library's typings for indexOf() 这是:

interface ReadonlyArray<T> {
indexOf(searchElement: T, fromIndex?: number): number;
}

这意味着 needle需要与 T 的元素属于同一类型 ( haystack) .但是你想要你的 needle是一个更宽类型(你正在查看 string 是否在 "this" | "that" 元素的数组中)。所以 TypeScript 不会让你这样做:

function arrayContains<T extends U, U>(haystack: ReadonlyArray<T>, needle: U): needle is T {
return haystack.indexOf(needle) >= 0; // error!
// ~~~~~~ <-- U is not assignable to T
}

幸运的是,您可以安全地加宽 ReadonlyArray<T>ReadonlyArray<U> ,所以你可以这样写:

function arrayContains<T extends U, U>(haystack: ReadonlyArray<T>, needle: U): needle is T {
const widenedHaystack: ReadonlyArray<U> = haystack;
return widenedHaystack.indexOf(needle) >= 0;
}

那行得通!您也可以只使用 type assertion使编译器静音,如:

function arrayContains<T extends U, U>(haystack: ReadonlyArray<T>, needle: U): needle is T {
return haystack.indexOf(needle as T) >= 0; // assert
}

但我通常尽量避免断言,除非它们是必要的,而在这里它们不是。这取决于你。


所以让我们把它们放在一起:

function arrayContains<T extends U, U>(haystack: ReadonlyArray<T>, needle: U): needle is T {
const widenedHaystack: ReadonlyArray<U> = haystack;
return widenedHaystack.indexOf(needle) >= 0;
}

function otherFunction(myObj: TSomeObject, field: keyof TSomeObject, newValue: string): TSomeObject {
const editableFields = ['title', 'description'] as const;
if (arrayContains(editableFields, field)) {
myObj[field] = newValue
}
return myObj
}

好的,希望对你有帮助。祝你好运!

Link to code

关于TypeScript 没有正确检查对象属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58331993/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com