#Return type based on input.

26 messages · Page 1 of 1 (latest)

fringe crown
#

Is something like this possible? Not sure why it doesn't work.

austere sluiceBOT
#
digital39999#0

Preview:```ts
type Return<T, A, B, C> = [A, B, C] extends [
string,
string,
string
]
? T
: T[]
declare function test<
T,
A = unknown,
B = unknown,
C = unknown

(data: {a: A; b: B; c: C}): Return<T, A, B, C>

// if all 3 properties are string, return T, if not T[]
const variable = test<number>({a: "", b: "", c: ""})
...```

signal oak
#

You cannot specify one generic type parameter while inferring the others, you have to either all specify or all infer.

#

But even putting that aside, I don't see how you can implement such a function, it's impossible to know what T is so there's no way for the function to return a T.

fringe crown
#

i've already implemented it just this wasn't working, didnt know that you cant do half half tbh

signal oak
#

I don't see how it's possible to implement it.

fringe crown
#

i mean not naturally, i had to do as Type but why wouldn't it be

signal oak
#

Let's pretend the partial inference issue does not exist, the following code:

const foo = test<number>({ a: '', b: '', c: '' })
const bar = test<string>({ a: '', b: '', c: '' })

Compiles to:

const foo = test({ a: '', b: '', c: '' })
const bar = test({ a: '', b: '', c: '' })

They are literally the same code, yet somehow foo should be number but bar has to be a string.

#

If T was somewhere in the arguments or somehow captured, it would be possible.

fringe crown
#

test function wraps the fetch function

#

so im just narrowing what i know itll be

signal oak
#

Okay yeah that's a classic case of

#

!:thats*

austere sluiceBOT
#
tjjfvi#0
`!tjjfvi:thats-no-generic-its-a-type-cast`:

When you use a type parameter in the function, it should generally be inferrable from the arguments.

For example, this is a misuse of generics:

function getMeA<T>(): T {
   /* magic */
}

because getMeA<string>() and getMeA<number>() compile to the same code at runtime, there's so way to implement this function safely (other than always throwing); this is just a type cast in disguise. Instead of using a generic here, you should return unknown, and cast at the call site if necessary, to be clear it's an unsafe operation:

-function getMeA<T>(): T {
+function getMeA(): unknown {
    /* magic */
 }

-getMeA<number>()
+getMeA() as number

One exception to this rule is if you're returning a possibly-empty container of T. For example, these are all perfectly safe, even though the generic can't be inferred from the parameters:

function emptyArray<T>(): Array<T> {
  return []
}

function useRef<T>(): { current?: T } {
  return {}
}
signal oak
#

A few API does that and it's actually unsafe, but it is convenient.

fringe crown
#

well yeah im pretty much aware that its unsafe but it is what it is

#

in this specific usecase i dont prefer using as type when calling function everywhere

signal oak
#

Cool, I wasn't sure if you were aware because it's just impossible to implement. I suppose I should've said "impossible to implement safely/correctly."

fringe crown
#

its easiest to just type it as unknown, but this is just for convenience so yeah, thanks tho

#

but now funny thing is that i can't know if a,b,c are or will be undefined or string xD

#

there goes my convenience out of the window

signal oak
#

I mean, your safety is already out of the window anyways by using a generic as type cast.

fringe crown
#

well yeah but now its impossible to make it work

signal oak
#

So you might as well use overload rather than generic to do what you want.

fringe crown
#

yeah

signal oak
#

This would work:

declare function test<T>(data: { a: string, b: string, c: string }): T
declare function test<T>(data: { a: unknown, b: unknown, c: unknown }): T[]