打字稿类型条件扩展对象

typescript type conditionals extends object

我想要有条件类型,即某物是一个对象。

type Id = {
    id: number
    obj: {
        x: 5
    }
}

type ObjString<T> = {
    [P in keyof T]:
        T[P] extends Object ? string : T[P]
}


const f: ObjString<Id> = {
    id: 4,
    obj: "xxxx"
}

这里,obj 属性 正确映射到 f 对象中的字符串,但是对于 id 属性,我会得到此错误消息:

error TS2322: Type 'number' is not assignable to type 'string'.

这意味着 T[P] extends Object 对于 number 的计算结果为真。我应该如何构造条件,以便 number 的计算结果为 false,而 object 的计算结果为 true?

这是因为Object类型太笼统了。 javascript 中的每个原始值都扩展了 Object,因为即使是数字和字符串也有自己的方法。 考虑这个例子:

type Check1 = string extends Object ? true : false // true
type Check2 = number extends Object ? true : false // true

我觉得换个角度解决问题比较好。您可以检查类型是否为原始类型:

type Id = {
  id: number
  obj: {
    x: 5
  }
}

type Primitives = string | number | boolean | bigint | symbol;

type ObjString<T> = {
  [P in keyof T]:
  T[P] extends Primitives ? T[P] : string
}

const f: ObjString<Id> = {
  id: 4,
  obj: "xxxx"
}

Playground

Object 类型并不真正对应于 TypeScript 中的非基元;相反,它是可以像对象一样被索引到的值的类型。这包括像 stringnumber 这样的基元,它们得到 wrapped with objects when you index into them (and thus supports things like "hello".toUpperCase() and (123).toFixed(2)). Only null and undefined are not Objects in this sense. Object in TypeScript is rarely what you want.

如果你想在 TypeScript 中找到一个意味着“非原始”的类型,你可以使用 the object type 代替(以小写 o 开头而不是大写 O):

type ObjString<T> = {
  [P in keyof T]:
  T[P] extends object ? string : T[P]
}

type Z = ObjString<Id>;
/* type Z = {
    id: number;
    obj: string;
} */

const f: ObjString<Id> = {
  id: 4,
  obj: "xxxx"
}

然后一切都按照你想要的方式进行。

请记住,数组和函数也是 object,因此您可能会遇到一些不良行为,具体取决于您想在那里看到的内容:

type Hmm = ObjString<{ foo(): string, bar: number[] }>;
// type Hmm = { foo: string; bar: string; }

Playground link to code