这里发生了一些事情。
一个是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 应该是DefinitelyReadableButNotNecessarilyWritableArray 而Array 应该是DefinitelyBothReadableAndWritableArray。也许不是。)
这是一个通用的用户定义类型保护,它采用某种类型的haystack 数组Array<T>,以及某种类型的needle U,它比T 宽,如果它返回@987654355 @,编译器理解为needle is T。 (它也认为false暗示needle是不是T,might not always be desirable。在你的情况下,因为T和U都将是字符串文字或它们的联合,这不是问题。)
如果我们有 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 需要与haystack 的元素属于同一类型(T)。但是您希望您的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