Typescript 是否可以根据传入的对象推断对象类型?

Typescript 是否可以根据传入的对象推断对象类型?,typescript,typescript-generics,Typescript,Typescript Generics,我有各种文档,它们都共享一组公共字段 我试图创建一个函数,它将填充公共字段,同时将唯一字段作为输入,从而生成一个完整的文档 我有这个工作,但现在我需要手动指定最终的类型。我很好奇typescript中是否有方法根据传入的字段推断最终类型(或者如果传入的字段与任何已知文档类型都不匹配,则抛出错误) 以下是我目前掌握的代码: 类型DocA={ id:string, createdAt:string, onlyDocA:string } 类型DocB={ id:string, createdAt:st

我有各种文档,它们都共享一组公共字段

我试图创建一个函数,它将填充公共字段,同时将唯一字段作为输入,从而生成一个完整的文档

我有这个工作,但现在我需要手动指定最终的类型。我很好奇typescript中是否有方法根据传入的字段推断最终类型(或者如果传入的字段与任何已知文档类型都不匹配,则抛出错误)

以下是我目前掌握的代码:

类型DocA={
id:string,
createdAt:string,
onlyDocA:string
}
类型DocB={
id:string,
createdAt:string,
onlyDocB:string
}
键入AllDocTypes=DocA | DocB
导出常量createBaseDocument=(
字段:省略,
):DocType=>{
const createdAt=新日期();
返回{
id:generateId(),
createdAt:createdAt.toString(),
…字段,
}作为DocType
}
函数generateId():字符串{
返回“id”
}
//testA应为'DocA'类型`
const testA=createBaseDocument({id:'a',createdAt:'123',onlyDocA:'hello'})
//testA应为'DocB'类型`
const testB=createBaseDocument({id:'a',createdAt:'123',onlyDocB:'hello'})
//testC应该抛出一个类型错误
const testC=createBaseDocument({id:'a',createdAt:'123',onlyDocC:'hello'})
//testD应该抛出一个类型错误
const testD=createBaseDocument({id:'a',createdAt:'123',onlyDocA:'hello',onlyDocB:'hello'})

稍微颠倒一下你的逻辑就行了。不幸的是,我不知道你的方法失败的确切原因(但是)

接口DocBase{
id:字符串
createdAt:string
}
仅接口Doca{
onlyDocA:string
}
仅接口DOCB{
onlyDocB:string
}
类型only=OnlyDocA | OnlyDocB
函数createBaseDocument(字段:T):DocBase&T{
const createdAt=新日期();
返回{
id:generateId(),
createdAt:createdAt.toString(),
…字段,
}
}
函数generateId():字符串{
返回“id”
}
const x=createBaseDocument({onlyDocA:'a'})


它也不允许传递像
{onlyDocX:1}
这样的随机内容,除非您以前定义过它。

如果您想更好地控制函数中的内容,您可能需要使用函数重载:

type DocA = {
  id: string,
  createdAt: string,
  onlyDocA: string
}

type DocB = {
  id: string,
  createdAt: string,
  onlyDocB: string
}

function createBaseDocument(fields: Omit<DocA, 'id'| 'createdAt'>): DocA;
function createBaseDocument(fields: Omit<DocB, 'id'| 'createdAt'>): DocB;
function createBaseDocument(fields: Omit<DocA, 'id'| 'createdAt'>|Omit<DocB, 'id'| 'createdAt'>): DocB|DocA{
  const createdAt = new Date();
  return {
    id:generateId(),
    createdAt: createdAt.toString(),
    ...fields
  }
}
类型DocA={
id:string,
createdAt:string,
onlyDocA:string
}
类型DocB={
id:string,
createdAt:string,
onlyDocB:string
}
函数createBaseDocument(字段:省略):DocA;
函数createBaseDocument(字段:省略):DocB;
函数createBaseDocument(字段:省略|省略):DocB | DocA{
const createdAt=新日期();
返回{
id:generateId(),
createdAt:createdAt.toString(),
…田地
}
}
这样
createBaseDocument({onlyDocA:'a'})
createBaseDocument({onlyDocB:'b'})
就可以了,但是
createBaseDocument({onlyDocA:'a',onlyDocB:'b'})
将导致一个错误

来扩展's,并解释为什么:


您的方法失败了,因为
Omit

我发现这不能防止发送随机内容,如这里所示,我可以从A、B和不在A或B中的内容发送:
const x=createBaseDocument({onlyDocA:'A',onlyDocB:'B',onlyDocX:'x'})
@ChrisDrackett-这是因为编译器被迫从对象推断类型参数值,而不是相反-您可以简单地提供显式注释以使其工作(毕竟,解决方案工作正常)。例如:
createBaseDocument({onlyDocA:“a”})
啊,这是有道理的。我怀疑是
省略
造成的,但不确定确切的机制。谢谢你的解释!@LazarLjubenović-是的,这是一种双重罪魁祸首:)如果不是泛型类型参数中的联合,这种方法就会奏效。但是,由于(我相信你知道,但OP可能错过了),GTP并没有得到很好的解决,所以根本就没有什么共享属性可以说了。感觉这让很多人都大吃一惊-我在写答案时有强烈的似曾相识的感觉。有没有一种方法可以让我在添加文档类型时自动执行这一操作,这样我就不必再添加更多的覆盖?恐怕没有,或者至少我不知道。
type DocA = {
  id: string,
  createdAt: string,
  onlyDocA: string
}

type DocB = {
  id: string,
  createdAt: string,
  onlyDocB: string
}

function createBaseDocument(fields: Omit<DocA, 'id'| 'createdAt'>): DocA;
function createBaseDocument(fields: Omit<DocB, 'id'| 'createdAt'>): DocB;
function createBaseDocument(fields: Omit<DocA, 'id'| 'createdAt'>|Omit<DocB, 'id'| 'createdAt'>): DocB|DocA{
  const createdAt = new Date();
  return {
    id:generateId(),
    createdAt: createdAt.toString(),
    ...fields
  }
}