-
Notifications
You must be signed in to change notification settings - Fork 674
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Friendlier TypeScript errors. #1002
Changes from all commits
7870a45
0647c3c
8e65bd9
41e2e63
671c4e7
089b302
103d6b9
ea8346f
8cfdfad
bebb666
120192f
8722aab
30c522f
89253aa
8cfd2ef
00ff818
a5ad143
9033499
3836ea1
92b13ec
26017d4
b4a883f
0756514
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,9 +3,8 @@ import { | |
ThemeContext as EmotionContext, | ||
InterpolationWithTheme, | ||
} from '@emotion/core' | ||
// @ts-ignore | ||
import { css, Theme } from '@theme-ui/css' | ||
import React from 'react' | ||
import * as React from 'react' | ||
import deepmerge from 'deepmerge' | ||
import { version as __EMOTION_VERSION__ } from '@emotion/core/package.json' | ||
|
||
|
@@ -72,8 +71,14 @@ const arrayMerge = (destinationArray, sourceArray, options) => sourceArray | |
export const merge = (a: Theme, b: Theme): Theme => | ||
deepmerge(a, b, { isMergeableObject, arrayMerge }) | ||
|
||
merge.all = <T = Theme>(...args: Partial<T>[]) => | ||
deepmerge.all<T>(args, { isMergeableObject, arrayMerge }) | ||
function mergeAll<A, B>(a: A, B: B): A & B | ||
function mergeAll<A, B, C>(a: A, B: B, c: C): A & B & C | ||
function mergeAll<A, B, C, D>(a: A, B: B, c: C, d: D): A & B & C & D | ||
function mergeAll<T = Theme>(...args: Partial<T>[]) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a few overloads to infer the type without explicit annotation. |
||
return deepmerge.all<T>(args, { isMergeableObject, arrayMerge }) | ||
} | ||
|
||
merge.all = mergeAll | ||
|
||
interface BaseProviderProps { | ||
context: ContextValue | ||
|
@@ -109,7 +114,7 @@ export function ThemeProvider({ theme, children }: ThemeProviderProps) { | |
const context = | ||
typeof theme === 'function' | ||
? { ...outer, theme: theme(outer.theme) } | ||
: merge.all<ContextValue>({}, outer, { theme }) | ||
: merge.all({}, outer, { theme }) | ||
|
||
return jsx(BaseProvider, { context }, children) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,28 @@ | ||
import { CSSObject, ThemeUIStyleObject, UseThemeFunction, Theme } from './types' | ||
import { | ||
CSSObject, | ||
ThemeUIStyleObject, | ||
ThemeDerivedStyles, | ||
Theme, | ||
ThemeUICSSObject, | ||
} from './types' | ||
|
||
export * from './types' | ||
|
||
export function get( | ||
obj: object, | ||
key: string | number, | ||
key: string | number | undefined, | ||
def?: unknown, | ||
p?: number, | ||
undef?: unknown | ||
): any { | ||
const path = key && typeof key === 'string' ? key.split('.') : [key] | ||
for (p = 0; p < path.length; p++) { | ||
obj = obj ? (obj as any)[path[p]] : undef | ||
obj = obj ? (obj as any)[path[p]!] : undef | ||
} | ||
return obj === undef ? def : obj | ||
} | ||
|
||
export const defaultBreakpoints = [40, 52, 64].map(n => n + 'em') | ||
export const defaultBreakpoints = [40, 52, 64].map((n) => n + 'em') | ||
|
||
const defaultTheme = { | ||
space: [0, 4, 8, 16, 32, 64, 128, 256, 512], | ||
|
@@ -215,20 +221,23 @@ const transforms = [ | |
{} | ||
) | ||
|
||
const responsive = (styles: Exclude<ThemeUIStyleObject, UseThemeFunction>) => ( | ||
theme?: Theme | ||
) => { | ||
const next: Exclude<ThemeUIStyleObject, UseThemeFunction> = {} | ||
const responsive = ( | ||
styles: Exclude<ThemeUIStyleObject, ThemeDerivedStyles> | ||
) => (theme?: Theme) => { | ||
const next: Exclude<ThemeUIStyleObject, ThemeDerivedStyles> = {} | ||
const breakpoints = | ||
(theme && (theme.breakpoints as string[])) || defaultBreakpoints | ||
const mediaQueries = [ | ||
null, | ||
...breakpoints.map(n => `@media screen and (min-width: ${n})`), | ||
...breakpoints.map((n) => `@media screen and (min-width: ${n})`), | ||
] | ||
|
||
for (const key in styles) { | ||
const value = | ||
typeof styles[key] === 'function' ? styles[key](theme) : styles[key] | ||
for (const k in styles) { | ||
const key = k as keyof typeof styles | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A type of key in X isn't always assignable to keys of type of X, even if we forget about the prototype. This part of TypeScript is a bit inconvenient, but it does help with some bugs. Explanation by Anders Hejlsberg: |
||
let value = styles[key] | ||
if (typeof value === 'function') { | ||
value = value(theme || {}) | ||
} | ||
|
||
if (value == null) continue | ||
if (!Array.isArray(value)) { | ||
|
@@ -243,7 +252,7 @@ const responsive = (styles: Exclude<ThemeUIStyleObject, UseThemeFunction>) => ( | |
} | ||
next[media] = next[media] || {} | ||
if (value[i] == null) continue | ||
next[media][key] = value[i] | ||
;(next[media] as Record<string, any>)[key] = value[i] | ||
} | ||
} | ||
|
||
|
@@ -259,22 +268,23 @@ export const css = (args: ThemeUIStyleObject = {}) => ( | |
...defaultTheme, | ||
...('theme' in props ? props.theme : props), | ||
} | ||
let result = {} | ||
let result: CSSObject = {} | ||
const obj = typeof args === 'function' ? args(theme) : args | ||
const styles = responsive(obj)(theme) | ||
|
||
for (const key in styles) { | ||
const x = styles[key] | ||
const x = styles[key as keyof typeof styles] | ||
const val = typeof x === 'function' ? x(theme) : x | ||
|
||
if (key === 'variant') { | ||
const variant = css(get(theme, val))(theme) | ||
const variant = css(get(theme, val as string))(theme) | ||
result = { ...result, ...variant } | ||
continue | ||
} | ||
|
||
if (val && typeof val === 'object') { | ||
result[key] = css(val)(theme) | ||
// TODO: val can also be an array here. Is this a bug? Can it be reproduced? | ||
result[key] = css(val as ThemeUICSSObject)(theme) | ||
continue | ||
} | ||
|
||
|
@@ -284,8 +294,8 @@ export const css = (args: ThemeUIStyleObject = {}) => ( | |
const transform: any = get(transforms, prop, get) | ||
const value = transform(scale, val, val) | ||
|
||
if (multiples[prop]) { | ||
const dirs = multiples[prop] | ||
if (prop in multiples) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line is a nice example of how sometimes common JS doesn't translate to strict TypeScript 😞 I'd guess the reasoning is: Since we don't know if |
||
const dirs = multiples[prop as keyof typeof multiples] | ||
|
||
for (let i = 0; i < dirs.length; i++) { | ||
result[dirs[i]] = value | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See facebook/react#18102
It might help with #1000. I didn't test it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably consider using star import for React everywhere.