Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.3k views
in Technique[技术] by (71.8m points)

typescript - Infer one of generic types from function argument

Consider the following example. fetchItems function returns response or response body depending on passed onlyBody argument which defaults to true.

interface HttpResponse<T> {
  body: T
}

function fetchItems<T, B extends boolean>(url: string, onlyBody: B = true as B) {
  return Promise
    .resolve({body: 'some data'} as any)
    .then<B extends true ? T : HttpResponse<T>>(res => onlyBody ? res.body : res);
}

If both generic types are passed the function works as expected

const a = fetchItems<string, false>('url', false) // Promise<HttpResponse<string>>
const b = fetchItems<string, true>('url', true)   // Promise<string>
const c = fetchItems<string, true>('url')         // Promise<string>

I'd like to drop requirement of passing B type as it is redundant in relation to onlyBody parameter. But when B type is not explictly passed, ts compiler complains about it (Expected 2 type arguments but got 1).

const e = fetchItems<string>('url', false);        // would want Promise<HttpResponse<string>>
const f = fetchItems<string>('url', true)          // would want Promise<string>
const g = fetchItems<string>('url')                // would want Promise<string>

I tried to change fucntion signature into:

function fetchItems<T, B extends boolean = true>(url: string, onlyBody: B = true as B) {

but then there is an error in e example: Argument of type 'false' is not assignable to parameter of type 'true | undefined'

Is there any way to alter function signature so that e, f, g examples will work the same as a, b, c ? demo: https://stackblitz.com/edit/typescript-ydkmzk

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Function overloads can do what you need:

function fetchItems<T>(url: string, onlyBody: false): Promise<HttpResponse<T>>
function fetchItems<T>(url: string, onlyBody?: true): Promise<T>
function fetchItems<T>(url: string, onlyBody: boolean = true) {
  return Promise
    .resolve({body: 'some data'} as any)
    .then(res => onlyBody ? res.body : res);
}

Playground

Solution with conditional types does not work due to TypeScript "design limitation" described here.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...