vanilla-extract-css / vanilla-extract

Zero-runtime Stylesheets-in-TypeScript

Home Page:https://vanilla-extract.style

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Typescript can't resolve which overload to use in `createTheme()` which makes it so we don't have autocomplete.

nasheomirro opened this issue · comments

Describe the bug

Typescript doesn't seem to resolve to the correct overload signature when calling createTheme():

// try this on the reproduction link
const vars = createThemeContract({
    a: null,
    b: null
});

const theme = createTheme(vars, {
  // typescript doesn't give suggestions
})

I think its because typeof vars can extend both Tokens and Contract, so typescript can't choose the correct overload from the first argument passed, so it relies on the second argument to distinguish them, but since both signatures throw an error at that part, the user's mistake can either be that the user didn't give the correct parameter of type "string" or the user supplied an object with missing/incorrect properties.

I'm guessing it doesn't give out suggestions for the second error because of the first one.

Possible solutions

This one can be done on the user's side. Create another function that just returns createTheme() but will always expect two arguments, contract and token. This is a little tedious cause vanilla-extract doesn't export the internal types needed so I think the only way is to infer/copy those types somehow.

// imagine I infered the types correctly
function createThemeFrom<T extends Contract>(
	contract: T,
	tokens: MapLeafNodes<T, string>,
	id?: string
) {
	return createTheme(contract, tokens, id);
}

Another one just for the user is to steal the MapLeafNodes<T, Leaf> utility and make your own:

type Theme<T> = MapLeafNodes<T, string>

// then use it like:
const vars = createThemeContract({ a: null, b: null });
const lightTheme: Theme<typeof vars> = {
  // ...
}

const theme = createTheme(vars, lightTheme);

A weird but easy fix I found was to change the order of the function declarations, putting the contract one first. I'm guessing maybe when errors are raised from multiple overload matches, the "suggestions" default to the first declaration:

// simplified example:
type JustMaps<T> = { [K in keyof T]: T[K] };

declare function example<T>(t: T, obj: JustMaps<T>, id?: string): string
declare function example<T>(t: T, id?: string): void

const result = example({ a: '' }, {
    // has autocomplete!
});

Reproduction

https://www.typescriptlang.org/play?ts=4.5.5#code/JYWwDg9gTgLgBAbzgYygUwIYzQFQBZohoA0K6WuBRAwhAHYxQbLwC+cAZlBCHAEQABAG4Y6wADbiMAWjQAPRsxgB6ZAGc1fANwAoHcnpr4IqGrgBeMpmz5CaWgyYsAFAh1wPcDAC44dAK6SxO6eAEa+AZI6rACUuvqG8DBUaBZWFLZEziZqpG6ecMrKcMlo6HDAZnQQXv4wEAbg4mjY0XF6OkVwaqBg4sAcwGgAJnDyGE1o3jowAJ5gqQBS-kYAshhgagA8OAB8aUgA2gDSFXRwANZosxAccDgAur44Jw9wrPHDaMhS5Rz+dBYwHoYzkEz6aB2u2cMGepGAwwA-L4jFBgHQAOYxXxCCAInRfH4YP4AoEg8aTKEwuFwCChABWvmWaw22z28KRKMY6KxXLRmL0BjoRjg6DUgXglgpENcXl8AHJ5e88iEPF01BNUmBuKFmiA2vEugB3NDAKDDcSzEp4CD+DF4UjJSpwI3QC5qaaE36pf6AmDA87S5oAJipsPupDpjLgzJg602UI5yO63Mx2JT-IxBO+3s4pP95LBk1De2pEYqnIzPPTuPxCWF8FdUAuPLSQbQwdlPjgiuViFVhWKeAwZgwdQaPAh2AAhAagA

System Info

N/A

Used Package Manager

npm

Logs

No response

Validations

Another one just for the user is to steal the MapLeafNodes<T, Leaf> utility and make your own

This can also be done like this:

const vars = createThemeContract({ a: null, b: null });

type Vars = Parameters<typeof createTheme<typeof vars>>[1];

const lightTheme: Vars = {
  // ...
}

const theme = createTheme(vars, lightTheme);