TypeScript需要提供通用参数

时间:2018-07-04 12:00:05

标签: javascript typescript

我具有以下功能:

async function get<U>(url: string): Promise<U> {
    return getUrl<u>(url);
}

但是,可以这样称呼它(TS将U设置为any):

get('/user-url');

有没有一种方法可以定义此函数,使得它需要显式提供U,如

get<User>('/user-url');

5 个答案:

答案 0 :(得分:9)

对此没有内置的支持,但是我们可以设计一个方案,其中使用默认的泛型类型参数和条件类型不传递类型参数会产生错误。也就是说,我们将为U提供默认值void。如果默认值为U的实际值,那么我们将在函数中键入参数,而该参数实际上不应传递给它,以获取错误:

async function get<U = void>(url: string & (U extends void ? "You must provide a type parameter" : string)): Promise<U> {
    return null as any;
}

get('/user-url'); // Error Argument of type '"/user-url"' is not assignable to parameter of type '"You must provide a type parameter"'.

class User {}
get<User>('/user-url');

错误消息不是很理想,但是我认为它将使消息得到传播。

答案 1 :(得分:4)

function createCommand<P extends WhatEver = never>(a: P): P {
    a.whatEverProperty; // a is always instanceof WhatEver. Never is overlooked here
}

在大多数情况下,最简单的解决方案是将never指定为默认类型参数值。 never始终可分配给type参数,即使使用WhatEver对其进行了扩展,因此函数的主体也不受影响。但是,函数的调用者必须指定类型实参。

答案 2 :(得分:3)

您可以使用多个类型参数:

function contractType<T = void, U extends T = T>(value: unknown): U {
    return value as U
}

const example1: string = contractType(17) // error
const example2: string = contractType("value") // error
const example3: string = contractType<string>("value") // ok

https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-288902999

答案 3 :(得分:0)

另一种方法!需要一个仅用于类型推断的参数:

/**
 * getFoo gives you a well-typed Foo component
 * ```tsx
 * const foo = getFoo(config)
 * ```
 */
const getFoo = <Keys extends string>(
  _config: Record<Keys, unknown>
) => {
  return FooUntyped as FooType<Keys>
}

如果您有可用于此类型推断的对象,并且您的函数不会接受参数,那么这对您来说可能是一个很好的方法*从 TS v4.1.2< /em>

如果你没有一些现成的配置对象,调用它有点像“wtf”,但你会直接在调用它的地方得到一个错误,而不是更远的地方:

const foo = getFoo({} as Record<'my' | 'keys', unknown>)
<块引用>

这种设计/方法实际上在另一个位置修复了我设计中的一个小问题。我实际上也添加了一个额外的参数,我的 getFoo 称为 defaultProps,它是给定 Foo 组件的一些默认道具(Foo 是一个表单字段组件,配置键是有效的字段名称)

答案 4 :(得分:0)

这里的其他答案提供了一些不错的解决方法,让您的代码按照您想要的方式工作,但没有一个解决基本问题,即您试图进行不安全的强制转换以使类型检查器满意。

我没有你的 getUrl<T> 函数的来源,但我强烈怀疑它看起来很像

function getUrl<T>(url: string): Promise<T> {
  return fetch(url).then(resp => resp.json());
}

在这种情况下,Response#json() 返回 Promise<any>,您悄悄地将其强制转换为 Promise<T> —— 这是不安全的。当您编写 const user: User = await get("./user"); 时,类型检查器很乐意推断您想要返回 Promise<User> 并为您填写。即使您提供默认值,它也会这样做。

更安全的方法是让 getUrl 返回 unknown。这将迫使您在运行时使用类型保护检查您的返回值,或者至少显式转换它((await get("./user")) as User 等),在这种情况下,您的 linter 可能会因为不安全的转换而惩罚您。