#Contravariance issue

11 messages ยท Page 1 of 1 (latest)

timber masonBOT
#
sabisenpai#0

Preview:```ts
type QueryConfig<T> = {
getTs: () => Promise<T[]>
filterT?: (t: T) => boolean
doSomethingWithT?: (t: T) => void
}

const aQuery: QueryConfig<{include: boolean}> = {
getTs: async () => [
{include: true},
{include: false},
],
filterT: ({include}) => include
...```

drowsy grove
#

What would you fine folks recommend here? ๐Ÿ˜…

round kiln
#

you can make this typecheck if you give queriesRunner a type parameter:

timber masonBOT
#
mkantor#0

Preview:```ts
...
type Queries<T extends readonly unknown[]> = {
[K in keyof T]: QueryConfig<T[K]>
}

// Simplified usage

const queriesRunner = async <
T extends readonly unknown[]

(
queries: Queries<T>
): Promise<void> => {
await Promise.all(
queries.map(async query => {
const ts = await query.getTs()
const filteredTs = query.filterT
? ts.filter(query.filterT)
: ts
for (const t of filteredTs) {
query.doSomethingWithT?.(t)
}
})
)
}

queriesRunner([aQuery, bQuery])
...```

round kiln
#

although i might consider factoring things this way instead:

timber masonBOT
#
mkantor#0

Preview:```ts
...
const applyQuery = async <T>(
query: QueryConfig<T>
) => {
const ts = await query.getTs()
const filteredTs = query.filterT
? ts.filter(query.filterT)
: ts
for (const t of filteredTs) {
query.doSomethingWithT?.(t)
}
}

Promise.all([applyQuery(aQuery), applyQuery(bQuery)])```

drowsy grove
#

@round kiln I really like the applyQuery! Unfortunately I think I stripped down my example a bit too much ๐Ÿ˜… This is closer to reality:

timber masonBOT
#
sabisenpai#0

Preview:```ts
// singleton, consumed by a "router runtime" of sorts
type RouterConfigs = RouterConfig[];

// dozens of these
type RouterConfig = {
services: ServiceConfig[];
}

// hundreds of these
type ServiceConfig = {
// ... existing functionality

newFunctionality?: QueryConfig<unknown>;

...```

drowsy grove
#

Applying generics all the way down is probably what I'd do if this was being written from scratch, but retrofitting it in is a massive sweeping change and this new functionality is meant to be completely opt-in, zero changes to anything not using it

round kiln
#

i'm also generally unsure why/how the new stuff you shared (ServiceConfig, RouterConfig, etc) are relevant. in your original playground you had a queriesRunner function which took multiple QueryConfig objects as input. are you saying that's not what you really need, and instead you want a function to consume RouterConfigs or somesuch?