匹配任何对象但不匹配数组的 TypeScript 类型

Col*_*ell 9 typescript

我试图定义一个与任何对象/字典匹配但不匹配数组的类型。

我的第一次尝试没有成功,因为从技术上讲,数组是底层的对象:

const a:{[k:string]: any} = []; // works fine
Run Code Online (Sandbox Code Playgroud)

我还知道可以创建一个通用的“检查器”,如下所示:

type NoArray<T> = T extends any[] ? never : T;
Run Code Online (Sandbox Code Playgroud)

但这不是我要找的。我想要一个像这样工作的非泛型类型:

const a: NoArrayType = {}; // works fine
const a: NoArrayType = []; // TypeError
Run Code Online (Sandbox Code Playgroud)

Kas*_*ter 8

这是我想到的,使用类型交集而不是索引签名

\n
/**\n * Constrains a type to something other than an array.\n */\nexport type NotArray = (object | string | bigint | number | boolean) & { length?: never; };\n
Run Code Online (Sandbox Code Playgroud)\n

这超出了原始海报所寻求的范围,但可以轻松调整:

\n
/**\n * Constrains a type to an object other than an array.\n */\nexport type NonArrayObject = object & { length?: never; };\n
Run Code Online (Sandbox Code Playgroud)\n

不使用索引签名的优点是,如果访问不存在的属性,则会出现错误:

\n
function test(hello: NonArrayObject) {\n    return hello.world; // error\n}\n
Run Code Online (Sandbox Code Playgroud)\n

using 的缺点\xe2\x80\xa6\xc2\xa0& { length?: never; }是您可以访问 的length属性NotArray,不能将其用于恰好具有length属性的对象,并且不能将其用于函数,因为它们具有length属性

\n

如果有人想知道,我用来NotArray定义可选的返回值,在大多数情况下,只有第一个返回值是令人感兴趣的:

\n
export type OptionalReturnValues2<T1 extends NotArray, T2> = T1 | [T1 | undefined, T2];\n\nexport function normalizeReturnValues2<T1 extends NotArray, T2>(optionalReturnValues: OptionalReturnValues2<T1, T2>): [T1 | undefined, T2 | undefined] {\n    if (Array.isArray(optionalReturnValues)) {\n        return optionalReturnValues;\n    } else {\n        return [optionalReturnValues, undefined];\n    }\n}\n\nexport type OptionalReturnValues3<T1 extends NotArray, T2, T3> = T1 | [T1 | undefined, T2] | [T1 | undefined, T2 | undefined, T3];\n\nexport function normalizeReturnValues3<T1 extends NotArray, T2, T3>(optionalReturnValues: OptionalReturnValues3<T1, T2, T3>): [T1 | undefined, T2 | undefined, T3 | undefined] {\n    if (Array.isArray(optionalReturnValues)) {\n        return [optionalReturnValues[0], optionalReturnValues[1], optionalReturnValues[2]];\n    } else {\n        return [optionalReturnValues, undefined, undefined];\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n


Ale*_*yne 7

类型问题出any在类型声明中。any在大多数打字稿应用程序中通常是您想要避免的事情。

数组只是一个可以用数字键索引的对象,并且有一些额外的方法。事实上,您几乎可以为该类型分配任何非原始值。

const a: {[k:string]: any} = [1,2,3]; // works
const b: {[k:string]: any} = {a: 123}; // works
const c: {[k:string]: any} = () => { console.log(123) }; // works
const d: {[k:string]: any} = () => new AnyClass(); // works
Run Code Online (Sandbox Code Playgroud)

操场

这与您可以执行以下操作的原因相同,因为这any是打字稿始终允许您将值转换为的一种情况。

const a: any = true
const b: any = {}
const c: any = new AnyClass()
Run Code Online (Sandbox Code Playgroud)

操场

所以你有几个选择。

  1. 限制你的类型,这样你就不会转换为any. 如果您知道这些属性的可能值,请声明它们。
interface MyObjectType { [k: string]: number | string }
const a: MyObjectType = [] // fails
const b: MyObjectType = {} // works
Run Code Online (Sandbox Code Playgroud)

操场

或者也许这就是JSON?如果是这样,any则不是正确的类型,因为您知道它不能包含某些内容(例如类实例或函数)。

interface Json {
  [key: string]: string | number | boolean | Json | Json[]
}

const a: Json = [] // type error
const b: Json = {} // works
Run Code Online (Sandbox Code Playgroud)

操场

  1. 或者使用unknowntype 而不是any,这要求您在使用值之前在运行时检查类型。
interface MyObjectType { [k: string]: unknown }

const a: MyObjectType = [] // type error
const b: MyObjectType = { prop: 123 } // works

// b.prop type here is: unknown
b.prop.toUpperCase() // type error

if (typeof b.prop === 'string') {
  // a.prop type here is: string
  console.log(b.prop.toUpperCase()) // works
}
Run Code Online (Sandbox Code Playgroud)

操场


小智 5

type MapOnly = Record<任意键,未知>

  • 通过额外的支持信息可以改进您的答案。请[编辑]添加更多详细信息,例如引文或文档,以便其他人可以确认您的答案是正确的。您可以[在帮助中心](/help/how-to-answer)找到有关如何写出好的答案的更多信息。 (2认同)