TypeScript泛型仅使用默认值

4

我试图将默认值应用于一个泛型。但是每当没有提供泛型时,该泛型就会默认为给定参数的值。我想防止这种情况发生。这里有一个非常简单的例子:

type Data = Record<string, any>

const computeData = <D extends Data = {}>(input: D): D => {
  // Does some stuff to input
  return input;
}

const res = computeData<{ name: string }>({ name: 'john' })

在这种情况下,“res”的类型是预期的{ name: string }。但是如果没有提供泛型参数,我需要将其默认为{},这是作为泛型参数默认值的类型。
const computeData = <D extends Data = {}>(input: D): D => {
  // Does some stuff to input
  return input;
}

const res = computeData({ name: 'john' })

为了举例,我简化了这段代码。在这个示例的上下文中,这有点无意义。

编辑: 实际用例是一个网络模拟器,让用户添加对象以模拟graphql请求。我想给他们提供提供解析器类型的选项。但是,如果他们没有提供类型,则会从传递的初始数据中进行推断-我想避免这种情况。

定义一个泛型类 enter image description here

类型错误是因为对构造函数的初始调用推断出了泛型的类型 enter image description here

这能够工作是因为实例化类时给定了明确的类型 enter image description here


这显示了两个 {name: string}:https://www.typescriptlang.org/play?#code/C4TwDgpgBAIghsOUC8UBKEDGB7ATgEwB4BnYXASwDsBzAGijkpAD4AoVnS0qHAWzACuwCPEQoohGFAgAPYZXzFYCJKgDeAX2YAKKoOAAuWAEojU5MyhrWUKAHo7sbBCXFsvaKQEAzb1GDYUHpCNlC4EMACuJRBlPoA3Kwa7Jzc4cQAjOJ8+iIqhGpQlHAeRqQUNFBa2oXFpVAA5ABW2AAWlA1VxhzYXMBhLgBM2e65onA1RSUQRs1tHV2sQA - tevemadar
2
这将是一件非常困难的事情,让Typescript去做。你基本上是破坏了泛型函数推断类型的方式。虽然可能有可能实现,但不太可能很好看。我真的很好奇这里的实际用例是什么,因为我怀疑还有更好的方法。 - Alex Wayne
2
我同意@AlexWayne的观点,我认为使用必需的类型参数比试图绕过语言更符合TypeScript的风格。我的解决方案(发布为答案)并不完美,但完全可行。 - evelynhathaway
@AlexWayne 感谢您的留言。我已经编辑了帖子,包括实际用例,希望更真实地解释了我所希望实现的内容。条件类型似乎是可行的解决方案。 - wazzaday
1个回答

4

通过使用另一个类型参数并过滤默认值,您可以规避类型推论。甚至可以在不进行类型断言的情况下完成此操作。

示例代码

type Data = Record<string, any>

type NoInferData<DataType extends Data | void, InputType extends Data> = (
    DataType extends void
        // If it was void, there was no argument, so the type must be an empty object, or whatever you'd like
        ? Record<string, never>
        // Otherwise it should match the explcitly added data type
        : InputType
); 

const computeData = <
    // Type arugment for the type of Data you are providing
    DataType extends Data | void = void,
    // Type argument to store the inferred type of the input
    // - If its void, return the default type, in this example it's an empty object
    // - Otherwise, return the type of Data explicitly provided
    InputType extends Data = DataType extends void ? Record<string, never> : DataType
>(input: NoInferData<DataType, InputType>): NoInferData<DataType, InputType> => {
  // Does some stuff to input
  return input;
}


// Allows expliclty marking the data type
const explicitData = computeData<{ name: string }>({ name: 'john' })

// Doesn't allow data that doesn't match the explcit data type
const explicitDataError = computeData<{ pizza: string }>({ name: 'john' })

// Doesn't allow data that doesn't meet the default (no inferring)
const implictEmptyObjectError = computeData({ name: 'john' })

// Allows the default type of the empty object
const implictEmptyObject = computeData({})

TypeScript Playground


谢谢回复。看起来这个方案可行。我会尝试让它工作。 - wazzaday
1
已经让它工作了,但正如你所说,它并不美观。也许最好的方法就像你提到的那样,总是要求一些 const mockNetwork2 = new MockNetwork<Record<string, any>>() - wazzaday

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接