Skip to content

Commit 8546dd0

Browse files
authored
Convert @emotion/primitives-core to TypeScript (#2818)
* [wip] primitives-core TS migration * Finish converting primitives-core to TS * Update yarn.lock? * Implement @Andarist's feedback for primitives-core TS migration * Fix regression
1 parent 945169b commit 8546dd0

File tree

7 files changed

+87
-48
lines changed

7 files changed

+87
-48
lines changed

packages/primitives-core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
},
2222
"devDependencies": {
2323
"@emotion/react": "11.9.3",
24+
"@types/css-to-react-native": "^3.0.0",
2425
"react": "16.14.0"
2526
},
2627
"homepage": "https://emotion.sh",
Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
import transform from 'css-to-react-native'
1+
import transform, { Style } from 'css-to-react-native'
2+
import { AbstractStyleSheet } from './types'
23
import { interleave } from './utils'
34

45
// this is for handleInterpolation
56
// they're reset on every call to css
67
// this is done so we don't create a new
78
// handleInterpolation function on every css call
8-
let styles
9-
let generated = {}
9+
let styles: unknown[] | undefined
10+
let generated: Record<string, unknown> = {}
1011
let buffer = ''
11-
let lastType
12+
let lastType: string | undefined
1213

1314
function handleInterpolation(
14-
interpolation,
15-
i /*: number */,
16-
arr /*: Array<*> */
15+
this: unknown,
16+
interpolation: any,
17+
i: number,
18+
arr: any[]
1719
) {
1820
let type = typeof interpolation
1921

@@ -44,7 +46,7 @@ function handleInterpolation(
4446
if (lastType === 'string' && (isRnStyle || isIrrelevant)) {
4547
let converted = convertStyles(buffer)
4648
if (converted !== undefined) {
47-
styles.push(converted)
49+
styles!.push(converted)
4850
}
4951
buffer = ''
5052
}
@@ -58,13 +60,13 @@ function handleInterpolation(
5860
if (arr.length - 1 === i) {
5961
let converted = convertStyles(buffer)
6062
if (converted !== undefined) {
61-
styles.push(converted)
63+
styles!.push(converted)
6264
}
6365
buffer = ''
6466
}
6567
}
6668
if (isRnStyle) {
67-
styles.push(interpolation)
69+
styles!.push(interpolation)
6870
}
6971
if (Array.isArray(interpolation)) {
7072
interpolation.forEach(handleInterpolation, this)
@@ -74,10 +76,10 @@ function handleInterpolation(
7476

7577
// Use platform specific StyleSheet method for creating the styles.
7678
// This enables us to use the css``/css({}) in any environment (Native | Sketch | Web)
77-
export function createCss(StyleSheet /*: Object */) {
78-
return function css(...args) {
79+
export function createCss(StyleSheet: AbstractStyleSheet) {
80+
return function css(this: unknown, ...args: any[]) {
7981
const prevBuffer = buffer
80-
let vals
82+
let vals: any[]
8183

8284
// these are declared earlier
8385
// this is done so we don't create a new
@@ -89,7 +91,7 @@ export function createCss(StyleSheet /*: Object */) {
8991
if (args[0] == null || args[0].raw === undefined) {
9092
vals = args
9193
} else {
92-
vals = interleave(args)
94+
vals = interleave(args as [any, ...any[]])
9395
}
9496

9597
try {
@@ -111,7 +113,7 @@ export function createCss(StyleSheet /*: Object */) {
111113

112114
let propertyValuePattern = /\s*([^\s]+)\s*:\s*(.+?)\s*$/
113115

114-
function convertPropertyValue(style) {
116+
function convertPropertyValue(this: [string, string][], style: string): void {
115117
// Get prop name and prop value
116118
let match = propertyValuePattern.exec(style)
117119
// match[2] will be " " in cases where there is no value
@@ -121,14 +123,14 @@ function convertPropertyValue(style) {
121123
// be the whole string so we remove it
122124
match.shift()
123125
// yes i know this looks funny
124-
this.push(match)
126+
this.push(match as unknown as [string, string])
125127
}
126128
}
127129

128-
function convertStyles(str /*: string */) {
130+
function convertStyles(str: string): Style | undefined {
129131
if (str.trim() === '') return
130132

131-
const stylePairs = []
133+
const stylePairs: [string, string][] = []
132134

133135
const parsedString = str.split(';')
134136

@@ -137,9 +139,9 @@ function convertStyles(str /*: string */) {
137139
try {
138140
return transform(stylePairs)
139141
} catch (error) {
140-
const msg = error.message
142+
const msg = (error as { message?: string } | undefined)?.message
141143

142-
if (msg.includes('Failed to parse declaration')) {
144+
if (msg && msg.includes('Failed to parse declaration')) {
143145
const values = msg
144146
.replace('Failed to parse declaration ', '')
145147
.replace(/"/g, '')
Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,35 @@ import * as React from 'react'
22
import { interleave } from './utils'
33
import { ThemeContext } from '@emotion/react'
44
import { createCss } from './css'
5+
import { AbstractStyleSheet } from './types'
56

6-
let testOmitPropsOnComponent = prop => prop !== 'theme' && prop !== 'as'
7+
let testOmitPropsOnComponent = (prop: string) =>
8+
prop !== 'theme' && prop !== 'as'
79

8-
/*
9-
type CreateStyledOptions = {
10-
getShouldForwardProp: (cmp: React.ElementType) => (prop: string) => boolean
10+
interface CreateStyledOptions {
11+
getShouldForwardProp(cmp: React.ElementType): (prop: string) => boolean
1112
}
1213

13-
type StyledOptions = {
14-
shouldForwardProp?: (prop: string) => boolean
14+
interface StyledOptions {
15+
shouldForwardProp?(prop: string): boolean
16+
}
17+
18+
type StyledProps = Record<string, unknown> & {
19+
as?: React.ElementType
1520
}
16-
*/
1721

1822
export function createStyled(
19-
StyleSheet /*: Object */,
20-
{
21-
getShouldForwardProp = () => testOmitPropsOnComponent
22-
} /*: CreateStyledOptions */ = {}
23+
StyleSheet: AbstractStyleSheet,
24+
options?: CreateStyledOptions
2325
) {
26+
const getShouldForwardProp =
27+
options?.getShouldForwardProp ?? (() => testOmitPropsOnComponent)
28+
2429
const css = createCss(StyleSheet)
2530

2631
return function createEmotion(
27-
component /*: React.ElementType */,
28-
options /* ?: StyledOptions */
32+
component: React.ElementType,
33+
options?: StyledOptions
2934
) {
3035
let shouldForwardProp =
3136
options && options.shouldForwardProp
@@ -35,17 +40,17 @@ export function createStyled(
3540
shouldForwardProp || getShouldForwardProp(component)
3641
let shouldUseAs = !defaultShouldForwardProp('as')
3742

38-
return function createStyledComponent(...rawStyles) {
39-
let styles
43+
return function createStyledComponent(...rawStyles: any[]) {
44+
let styles: any[]
4045

4146
if (rawStyles[0] == null || rawStyles[0].raw === undefined) {
4247
styles = rawStyles
4348
} else {
44-
styles = interleave(rawStyles)
49+
styles = interleave(rawStyles as [any, ...any[]])
4550
}
4651

4752
// do we really want to use the same infra as the web since it only really uses theming?
48-
let Styled = React.forwardRef((props, ref) => {
53+
let Styled = React.forwardRef<unknown, StyledProps>((props, ref) => {
4954
const finalTag = (shouldUseAs && props.as) || component
5055

5156
let mergedProps = props
@@ -62,7 +67,7 @@ export function createStyled(
6267
? getShouldForwardProp(finalTag)
6368
: defaultShouldForwardProp
6469

65-
let newProps = {}
70+
let newProps: Record<string, unknown> = {}
6671

6772
for (let key in props) {
6873
if (shouldUseAs && key === 'as') continue
@@ -77,17 +82,26 @@ export function createStyled(
7782

7883
return React.createElement(finalTag, newProps)
7984
})
80-
Styled.withComponent = (newComponent /*: React.ElementType */) =>
81-
createEmotion(newComponent)(...styles)
8285

8386
Styled.displayName = `emotion(${getDisplayName(component)})`
8487

85-
return Styled
88+
const withComponent = (newComponent: React.ElementType) =>
89+
createEmotion(newComponent)(...styles)
90+
91+
const castedStyled = Styled as typeof Styled & {
92+
withComponent: typeof withComponent
93+
}
94+
95+
castedStyled.withComponent = withComponent
96+
97+
return castedStyled
8698
}
8799
}
88100
}
89101

90-
const getDisplayName = primitive =>
102+
const getDisplayName = (
103+
primitive: string | { displayName?: string; name?: string }
104+
) =>
91105
typeof primitive === 'string'
92106
? primitive
93107
: primitive.displayName || primitive.name || 'Styled'
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
type NamedStyles<T> = { [P in keyof T]: unknown }
2+
3+
// This is based on the StyleSheet type from @types/react-native
4+
export interface AbstractStyleSheet {
5+
create<T extends NamedStyles<T> | NamedStyles<any>>(
6+
styles: T | NamedStyles<T>
7+
): T
8+
9+
flatten(style?: unknown[]): unknown
10+
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
export function interleave(vals /*: Array */) {
1+
export function interleave(
2+
vals: [TemplateStringsArray, ...unknown[]]
3+
): unknown[] {
24
let strings = vals[0]
3-
let finalArray = [strings[0]]
5+
let finalArray: unknown[] = [strings[0]]
46
for (let i = 1, len = vals.length; i < len; i++) {
57
finalArray.push(vals[i])
68
if (strings[i] !== undefined) {

yarn.lock

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5880,6 +5880,11 @@
58805880
resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.10.tgz#61cc8469849e5bcdd0c7044122265c39cec10cf4"
58815881
integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==
58825882

5883+
"@types/css-to-react-native@^3.0.0":
5884+
version "3.0.0"
5885+
resolved "https://registry.yarnpkg.com/@types/css-to-react-native/-/css-to-react-native-3.0.0.tgz#63831e79c3855f78981217c745fb4bd7c8733d05"
5886+
integrity sha512-MjLLf4YKMQYKnZ7xPZisizAiC8C5EQUKSLeimtApbc82WJDDN0TQBCY+HhWJHsSQ8Le4509aD/O7MWV73jJykQ==
5887+
58835888
"@types/debug@^0.0.30":
58845889
version "0.0.30"
58855890
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-0.0.30.tgz#dc1e40f7af3b9c815013a7860e6252f6352a84df"
@@ -9168,11 +9173,16 @@ caniuse-api@^3.0.0:
91689173
lodash.memoize "^4.1.2"
91699174
lodash.uniq "^4.5.0"
91709175

9171-
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001010:
9176+
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001010:
91729177
version "1.0.30001012"
91739178
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001012.tgz#653ec635e815b9e0fb801890923b0c2079eb34ec"
91749179
integrity sha512-7RR4Uh04t9K1uYRWzOJmzplgEOAXbfK72oVNokCdMzA67trrhPzy93ahKk1AWHiA0c58tD2P+NHqxrA8FZ+Trg==
91759180

9181+
caniuse-lite@^1.0.30000844:
9182+
version "1.0.30001373"
9183+
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001373.tgz#2dc3bc3bfcb5d5a929bec11300883040d7b4b4be"
9184+
integrity sha512-pJYArGHrPp3TUqQzFYRmP/lwJlj8RCbVe3Gd3eJQkAV8SAC6b19XS9BjMvRdvaS8RMkaTN8ZhoHP6S1y8zzwEQ==
9185+
91769186
caniuse-lite@^1.0.30001109:
91779187
version "1.0.30001204"
91789188
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001204.tgz#256c85709a348ec4d175e847a3b515c66e79f2aa"
@@ -11874,9 +11884,9 @@ ejs@^3.1.6:
1187411884
jake "^10.6.1"
1187511885

1187611886
electron-to-chromium@^1.3.47:
11877-
version "1.3.212"
11878-
resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.212.tgz#15d81ba96edb8ae6f937cde0fdb18c1c3c2bfec5"
11879-
integrity sha512-H8z5Smi1s1u1zGegEBfbxUAzrxyk1JoRHHHrlNGfhxv3sTb+p/Jz7JDvrR4196Q/Ip8r4+XwWcLvKrUjFKoJAg==
11887+
version "1.4.206"
11888+
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.206.tgz#580ff85b54d7ec0c05f20b1e37ea0becdd7b0ee4"
11889+
integrity sha512-h+Fadt1gIaQ06JaIiyqPsBjJ08fV5Q7md+V8bUvQW/9OvXfL2LRICTz2EcnnCP7QzrFTS6/27MRV6Bl9Yn97zA==
1188011890

1188111891
elegant-spinner@^1.0.1:
1188211892
version "1.0.1"

0 commit comments

Comments
 (0)