#readonly unknown[] doesn't accept proper type

90 messages · Page 1 of 1 (latest)

last wharfBOT
#
onkeltem#0

Preview:```ts
type Client1 = {}
type Client2 = {}
type Clients = Client1 | Client2

type TupleIndices<T extends readonly unknown[]> =
Extract<
keyof T,
${number}

extends ${infer N extends number}
? N
: never

function asd<T>(
clients: [
...(T extends readonly Clients[] ? T : never)
]
) {
...```

ruby barn
#

Can anybody tell what is going in here?

#
Type 'T' does not satisfy the constraint 'readonly unknown[]'.(2344)
#

How comes? it's the same type

#

Another variant, w/o readonly:

last wharfBOT
#
onkeltem#0

Preview:```ts
type Client1 = {}
type Client2 = {}
type Clients = Client1 | Client2

type TupleIndices<T extends unknown[]> = Extract<
keyof T,
${number}

extends ${infer N extends number}
? N
: never

function asd<T>(
clients: [...(T extends Clients[] ? T : never)]
) {
...```

ruby barn
#

This one is even more confusing:

Type 'T' does not satisfy the constraint 'unknown[]'.(2344)
#

oops

#

wait

last wharfBOT
#
onkeltem#0

Preview:```ts
type Client1 = {}
type Client2 = {}
type Clients = Client1 | Client2

type TupleIndices<T extends any[]> = Extract<
keyof T,
${number}

extends ${infer N extends number}
? N
: never

function asd<T>(
clients: T extends Clients[] ? [...T] : never
) {
...```

ruby barn
#

the same...

#

If T extends Clients[] and it's a tuple [...T] then why it's not bloody any[]?

astral inlet
#

It still allows calling with any other generic type (but no args)

astral inlet
ruby barn
#

Ah, I see

#

I should have used: type A = TupleIndices<typeof clients>

astral inlet
#

Doubt that works

#

Why not asd<T extends readonly Clients[]> ?

ruby barn
#

I don't know, I will try @astral inlet

#

I think it won't work later on

#

Consider an example:

#
class Foo<T extends Clients[]> {
  clients: T
  constructor(clients: [...T]) {
    this.clients = clients
  }
}
#

Here clients will be tuple of just array?

#

I think they're gonna be just an array

#

So basically, T extends Clients[] constrain is not enough then.

#

But this one would make it a true tuple, right?

class Foo<T> {
  clients: T extends Clients[] ? [...T] : never
  constructor(clients: T extends Clients[] ? [...T] : never) {
    this.clients = clients
  }
}
astral inlet
#

tuples are always readonly arrays btw

ruby barn
#

But they should know about their elements, I suppose

#

arrays don't

last wharfBOT
#
jakoxb#0

Preview:```ts
type Client1 = {}
type Client2 = {}
type Clients = Client1 | Client2

type TupleIndices<T extends readonly any[]> = Extract<
keyof T,
${number}

extends ${infer N extends number}
? N
: never

function asd<T extends readonly Clients[]>(
...clients: T
) {
...```

ruby barn
#

Not sure if it's the same problem or not, but here is another snippet.
It's a big example, I tried to make it as simple as it's possible.
I think I could dissect it even more.

last wharfBOT
#
onkeltem#0

Preview:```ts
type Client1 = {run(options: {id: number}): void}
type Client2 = {run(options: {uuid: string}): void}
type Clients = Client1 | Client2

type TupleIndices<T extends readonly any[]> = Extract<
keyof T,
${number}

extends `${infer N extends number}
...```

ruby barn
#

The problem is:

Argument of type 'GetOptionsType<TClients[TClientId]> | undefined' is not assignable to parameter of type '{ id: number; } & { uuid: string; }'.
  Type 'undefined' is not assignable to type '{ id: number; } & { uuid: string; }'.
    Type 'undefined' is not assignable to type '{ id: number; }'.

in options in the 26th line

#

I thought that it's because of the tuple/array difference

#

The type turns into { id: number; } & { uuid: string; }

#

I suspect GetOptionsType is the culprit

#

Yeah:

type C1orC2 = GetOptionsType<Clients>;

gives: { id: number; } & { uuid: string; } 😕

#

Darn, I continue meticulously hit the same wall with my head with TypeScritp

#

Every time, just the same

#

I almost expect it to be "impossible"

#

I pass an index of the tuple. So it should know what it is. Then I ask it to take that index, apply it on the tuple, and get the function

#

as any seems to the most reasonable solution

spring monolith
#

you get the actual issue if you make it required

#

i think the issue here is that TClientId could be a union, so clientId might not actually align with options

#

that's an issue of the interface

#

it can't be made typesafe

ruby barn
#

No error here:

#

But it's not the same I suppose...

#

GetProps is not needed 🙂

last wharfBOT
#
onkeltem#0

Preview:```ts
type A = {id: number}
type B = {uuid: string}

type TupleIndices<T extends readonly any[]> = Extract<
keyof T,
${number}

extends ${infer N extends number}
? N
: never

class Foo<T extends readonly (A | B)[]> {
constructor(public items: [...T]) {}

foo<K ex
...```

spring monolith
#

that.. seems unsound

ruby barn
#

here is a shorter one

#

with function

#

Sorry, again..

last wharfBOT
#
onkeltem#0

Preview:```ts
type A = {run: (options: {id: number}) => void}
type B = {run: (options: {uuid: string}) => void}

type TupleIndices<T extends readonly any[]> = Extract<
keyof T,
${number}

extends ${infer N extends number}
? N
: never

type GetOptionsType<T extends A | B> =
T["run"] extends (options: infer K) => any
? K
: never
...```

ruby barn
#

I guess this one is the minimal test case I can come up with

last wharfBOT
#
that_guy977#0

Preview:ts ... const x = new Foo([{ id: 0 }, { uuid: "" }]); // ^? x.foo<0 | 1>(0, { uuid: "" }); console.log(x); // Foo: { "items": [{ "uuid": "" }, { "uuid": "" }] } // mismatch to type argument const wrong = x.items[0] // ^? console.log(wrong); // mismatch to type console.log(wrong.id.toString()); // runtime error ...

spring monolith
ruby barn
#

Omg, I see!

#

So how to do it in TS anyway? The task is obviously a real one. That TS extends K to allow for a union seems to be an issue of TS

#

Obviously I don't want it, and that's not what I was intended to do, right?

#

It should be possible to do this: x.foo<0 | 1>(0, { uuid: "" });

#

It must be either 0, or 1, but not at the same time

spring monolith
#

this kind of interface just doesn't work well in ts

ruby barn
#

What interface, key parameter in a generic?

spring monolith
#

"interface" as in "way to interact with thing"

#

having a method that takes 2 parameters that need to align in a key-value kind of way doesn't work well in ts

ruby barn
#

It also affects nested functions I guess

#

What would be a lesser evil in this case?
I can think about as any:

last wharfBOT
#
onkeltem#0

Preview:```ts
type A = {run: (options: {id: number}) => void}
type B = {run: (options: {uuid: string}) => void}

type TupleIndices<T extends readonly any[]> = Extract<
keyof T,
${number}

extends ${infer N extends number}
? N
: never

type GetOptionsType<T extends A | B> =
T["run"] extends (options: infer K) => any
? K
: never
...```

ruby barn
#
  this.items[index].run(options as any)
spring monolith
#

any is absolutely the greater evil

ruby barn
#

Meh

spring monolith
#

imo, redesign the interface

ruby barn
#

How? I pass a class a set of clients, it stores them. Then when I call run it should run one by index

#

It's btw an internal method, there will be another one, which calls it, it runs all of them

last wharfBOT
#
that_guy977#0

Preview:```ts
...
class Foo<T extends readonly (A | B)[]> {
constructor(public items: [...T]) {}

foo<K extends TupleIndices<T>>(index: K): T[K] {
return this.items[index]
}
}
...```

spring monolith
#

something like this would work

#

and since items is public, you don't even need foo

#

wait a minute.

#

oh nvm

ruby barn
#

Thanks @spring monolith I got it. But I've just realized another thing: I can't use options. As simple as that

#

Because how the heck I was going to runAll(), if they all takes different options

#

So all the options I should keep in the items, when they're being created

#

something like:

type A = { options: {id: number}, run: () => void };
type B = { options: {uuid: string}, run: () => void };
#

so they're free to use them, because they will belong to them then

spring monolith