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');
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
function createCommand<P extends WhatEver = never>(a: P): P['whatEverProperty'] {
// a is always instanceof WhatEver. Never is overlooked here.
return a.whatEverProperty as P extends WhatEver ?
? WhatEver
: never
}
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会因为不安全的强制转换而惩罚你。
import {toPath} from 'lodash-es'
import {pathOr} from 'ramda'
// Aternative to lodash `get` where you have to provide a default value and a return type that includes the default value.
// Useful to avoid cases where the return type is inferred from the default value's type but does not fit with the object's content at the path.
export const getOr = <T = never, D extends T = T>(object: unknown, pathStr: string, defaultValue: D): T =>
pathOr(defaultValue, toPath(pathStr), object)
6条答案
按热度按时间njthzxwz1#
没有内置的支持,但是我们可以设计一个场景,在那里不传入类型参数将生成一个错误,使用默认的泛型类型参数和条件类型。也就是说,我们将给予
U
一个默认值void
。如果默认值是U
的实际值,然后我们将参数输入到函数中,作为不应该真正传入的参数,从而得到一个错误:错误信息并不理想,但我认为它会传达信息。
编辑:有关在参数类型中使用类型参数的解决方案,请参见此处
yftpprvb2#
可以使用多个类型参数:
https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-288902999
3xiyfsfu3#
大多数情况下,最简单的解决方案是将
never
指定为默认类型参数值。never
总是可赋值给类型参数,即使您使用WhatEver扩展它,因此函数体不受影响,但此处的返回值将是never
,影响使用者以任何有意义的方式使用此函数的能力。(使用者/调用者在尝试使用返回值时会得到一个错误,但如果您的函数纯粹是为了副作用而调用的,您可能需要考虑@Titian的回答中概述的方法)
当通常不返回
P
时,可以使用条件类型使返回类型为never
:oxcyiej74#
还有一种方法!需要一个只用于类型推断的参数:
如果您有对象可随时用于传入此类型的推理,并且您的函数不接受参数,则这可能是一个很好的方法**从TS v4.1.2起 *
如果你没有现成的配置对象,调用它有点“wtf”,但你会直接在调用它的地方得到一个错误,而不是在更远的地方:
这种设计/方法碰巧在另一个地方修复了我设计中的一个小问题。我实际上在我的
getFoo
上添加了一个额外的参数,称为defaultProps
,这是给定Foo组件的一些默认属性(Foo是一个表单字段组件,配置键是有效的字段名)brvekthn5#
这里的其他答案提供了一些不错的变通方法,可以让代码按照您想要的方式工作,但没有一个解决了根本问题,即您试图进行不安全的强制转换以使类型检查程序满意。
我没有
getUrl<T>
函数的源代码,但我强烈怀疑它看起来很像在这个场景中,
Response#json()
返回Promise<any>
,您正在悄悄地将其强制转换为Promise<T>
--这是不安全的。当您编写const user: User = await get("./user");
时,类型检查器很乐意推断您想要返回一个Promise<User>
并为您填充它。即使您提供了默认值,它也会这样做。一个更安全的方法是让
getUrl
返回unknown
,这将迫使你在运行时用类型保护来检查你的返回值,或者至少显式地强制转换它((await get("./user")) as User
等),在这种情况下,你的linter会因为不安全的强制转换而惩罚你。u4dcyp6a6#
由于下面的Ramda pathOr类型定义,我偶然发现了您的问题:
该函数可以沿着路径查找对象内部深处的值。
问题是,如果你不提供泛型类型参数T,Typescript将使用defaultValue中的类型,当对象的内容在该路径中不具有相同的类型时,这是没有意义的。
这个解决方案很复杂,只有在有两个论点的情况下才有效。我不认为有一个简单的答案可以回答你的问题,适用于所有情况。
公平地说,ramda的path函数和lodash的get函数都非常努力地根据对象和路径找到正确的返回类型,但我看不出同时使用T类型作为默认值和返回值是个好主意。