#Generic Method not assignable due to use of unknown

1 messages · Page 1 of 1 (latest)

quasi bridge
#

Min repro

I need to create some number of Services, which are factory patterns returning a collection of methods in an object. Since I plan to reuse the Service type for several services, all with different methods, it is impossible to know their signatures in advance. Thus I want to have the methods typed as unknown[] => unknown, and then to extend that in a given service implementation with its actual signature(s). I cannot get this to work, I have tried several different combinations of generics, extending, whatnot. I did manage to make it work by swapping the unknown's for any's - but I dont believe this should be necessary here, and my linter hates it too. Any help would be appreciated

acoustic vigil
#

That's an empty TS playground link

#

You maybe need to replace unknown with never in the argument position

#

!:variance

hearty talonBOT
#
tjjfvi#0
`!t6:variance`:

Here's the example with Dog and Animal, explaining co/contra/in variance:

  • Covariance: () => Dog is assignable to () => Animal, because Dog is assignable to Animal; it "preserves the direction of the assignability"
  • Contravariance: (Animal) => void is assignable to (Dog) => void, because something that expects an Animal can also take a Dog; it "reverses the direction of the assignability"
  • Invariance: (Animal) => Animal is not assignable to (Dog) => Dog, because not all returned Animals are Dogs, and (Dog) => Dog is not assignable to (Animal) => Animal, because something expecting a Dog cannot take any other kind of Animal
quasi bridge
acoustic vigil
#

You need to copy the link after you add the code

#

The address will change, the content is encoded within

hearty talonBOT
acoustic vigil
#

A ServiceMethod basically can be any function?

quasi bridge
#

Yeah

acoustic vigil
#

any function is (args: never) => unknown

quasi bridge
#

Can you explain why that is? That doesn't make intuitive sense to me

acoustic vigil
#

yep let me think about it

#

wait

#

(...args: never) => unknown

#

So we can write a function like

(...args: [number, string]) => unknown

which is equal to

(arg0: number, arg1: string) => unknown
#

Because the tuple gets spread into the individual arguments

#

if we have a function type like

(...args: X) => unknown

it means "This function might be callable with args of type X, so X must be assignable to whatever it's arguments are.

#

So if X is [number | string], it is OK to call it with [number], because number is assignable to number | string and so [number] is assignable to [number | string]

#

never by definition is assignable to anything. So saying that never must be assignable to the args of the function [...] means that the function can have any arguments

hearty talonBOT
#
sandiford#0

Preview:```ts
declare const n: never

const s: string = n
const a: [number] = n```

acoustic vigil
#

no errors

quasi bridge
#

I'm not sure how to work that into the Service thing, I'm trying on the other monitor but I must be missing something

hearty talonBOT
#
sandiford#0

Preview:```ts
type UnknownFunction = (...args: never) => unknown

type Service<
S extends Record<string, UnknownFunction> = Record<
string,
UnknownFunction

= S

const CreateFooService = (): Service<{
foo: (arg: number) => number
}> => {
const foo = (fooArg: number): number => fooArg *
...```

acoustic vigil
#

I took out ServiceMethod as it didn't seem necessary

quasi bridge
#

oh so you don't even need to extend the never, it's just fine as is

acoustic vigil
#

😐

#

You extend the function that has never, so it's extending never inderectly

#

Well, technically the never is extending the real args, because the check for a function arg is reversed

quasi bridge
#

Yeah I see that, thanks for the help!

#

!resolved

acoustic vigil
#

🫡