isomorphic-style-loader
isomorphic-style-loader copied to clipboard
TypeScript Definition Help
Hi all,
I'm working on a TypeScript definition for withStyle
. What I have so far is:
type identity<T> = (arg: T) => T;
//TODO: This could be better, specific type information about the component is lost
export default function withStyles<P, S, T>(style: any): identity<new (...args: T[]) => React.Component<P, S>>
While this is OK, clearly some type information about the component is being lost, and I'm not too sure what type to use for the style object. Any ideas how to make this better?
Note that
export default function withStyles<T extends React.Component>(style: any): identity<T>
only works if you specify the return type (with withStyles<ComponentName>
). Trying to avoid explicit generics if possible.
@danielmhanover did you ever get this fully functional?
I just use
import withStyles from "isomorphic-style-loader/lib/withStyles"
export default function injectTSFriendlyStyles<T extends React.ComponentClass>(s: any, t: T): T {
return withStyles(s)(t) as T
}
@danielmhanover Did you create this in a .d.ts
file?
Your code gives me the following error:
TS7016: Could not find a declaration file for module 'isomorphic-style-loader/lib/withStyles'. '\node_modules\isomorphic-style-loader\lib\withStyles.js' implicitly has an 'any' type
and [ts] An implementation cannot be declared in ambient contexts.
I'm assuming you save it in a .tsx
file and then use it like injectTSFriendlyStyles(s,Widget)
This would preclude me from composing it with other hocs, such as injectIntl
however..
This seems to work fine for me in global.d.ts
declare module 'isomorphic-style-loader/lib/withStyles' {
export interface Styles {
[key: string]: string;
}
const withStyles = (style: Styles) => <T extends React.ComponentClass<P, S>>(
component: T,
): T => T;
export default withStyles;
}
declare module '*.scss' {
import { Styles } from 'isomorphic-style-loader/lib/withStyles';
const value: Styles;
export = value;
}
declare module '*.css' {
import { Styles } from 'isomorphic-style-loader/lib/withStyles';
const value: Styles;
export = value;
}
after this I am able to use withStyles
in my component as below
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import * as React from 'react';
import * as style from './ComponentStyle.scss';
@withStyles(style)
export class MyComponent extends React.Component {
// component definition
}
@sumit-sinha Tihs is getting closer. However it falls down when passing multiple styles, e.g.
withStyles( normalizeCss, antd,
also receive the warning a const initializer in an ambient context must be a string or numeric literal
This caters for multiple styles:
declare module 'isomorphic-style-loader/lib/withStyles' {
export interface Styles {
[key: string]: string
}
const withStyles = (...styles: Styles[]) => <
T extends React.ComponentClass<P, S>
>(
component: T,
): T => T
export default withStyles
}
declare module '*.scss' {
import { Styles } from 'isomorphic-style-loader/lib/withStyles'
const value: Styles
export = value
}
declare module '*.css' {
import { Styles } from 'isomorphic-style-loader/lib/withStyles'
const value: Styles
export = value
}
@damiangreen : when I try that, I get:
ERROR in [at-loader] ./global.d.ts:6:22
TS1254: A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.
ERROR in [at-loader] ./global.d.ts:7:36
TS2304: Cannot find name 'P'.
ERROR in [at-loader] ./global.d.ts:7:39
TS2304: Cannot find name 'S'.
ERROR in [at-loader] ./global.d.ts:10:11
TS2693: 'T' only refers to a type, but is being used as a value here.
Edit: Oops that didn't work. I've updated the snippet
I was getting the same errors as @flyrev . Maybe some differences in TS versions.
This should fix that and should also support functional components.
interface Styles {
[key: string]: string;
}
declare module 'isomorphic-style-loader/withStyles' {
function withStyles(
...styles: Styles[]
): <P, S, T extends React.ComponentClass<P, S> | React.FunctionComponent<P>>(
component: T
) => T;
export = withStyles;
}
declare module '*.scss' {
const value: Styles;
export = value;
}
declare module '*.css' {
const value: Styles;
export = value;
}
@NoxHarmonium 's answer works well.
For completeness we had to add
declare module 'isomorphic-style-loader/useStyles' {
const useStyles = (...styles: Styles[]) => any
export default useStyles
}
@damiangreen I'm getting a pretty cryptic ts error:
1254: A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.
Any chance you know what that might be?
nvm figured it out:
declare module 'isomorphic-style-loader/useStyles' {
function useStyles(...styles: Styles[]): void;
export = useStyles
}
Following the thread, this is a sugestion for *.[s]css
files, useStyles
hook and StyledContext
in global.d.ts
file:
type Dispose = () => void
type InsertCssItem = () => Dispose
type GetCSSItem = () => string
type GetContent = () => string
interface Style {
[key: string]: InsertCssItem | GetCSSItem | GetContent | string
_insertCss: InsertCssItem
_getCss: GetCSSItem
_getContent: GetContent
}
declare module '*.scss' {
const style: Style
export default style
}
declare module '*.css' {
const style: Style
export default style
}
declare module 'isomorphic-style-loader/useStyles' {
function useStyles(...styles: Style[]): void
export default useStyles
}
declare module 'isomorphic-style-loader/StyleContext' {
import { Context } from 'react'
type RemoveGlobalCss = () => void
type InsertCSS = (...styles: Style[]) => RemoveGlobalCss | void
interface StyleContextValue {
insertCss: InsertCSS
}
const StyleContext: Context<StyleContextValue>
export { StyleContext as default, InsertCSS }
}
Hello @danielmhanover, are you still working on the typescript? I am expecting @types/isomorphic-style-loader
.
I tried with above suggestions but still seeing issues. Would you or anyone has a working solution?
Thanks in advance.
Building on @NoxHarmonium I got this working with less. I made some minor changes to get everything to work with Functional components. Thank you for the solution. I hope this helps someone ...
// global.d.ts
interface Styles {
[key: string]: string;
}
declare module 'isomorphic-style-loader/withStyles' {
function withStyles(
...styles: Styles[]
): <P, S>(
component: React.FunctionComponent<P> | React.ComponentClass<P, S>,
) => React.FunctionComponent<P> | React.ComponentClass<P, S>;
export = withStyles;
}
declare module '*.less' {
const value: Styles;
export = value;
}
declare module '*.css' {
const value: Styles;
export = value;
}
// SomeComponent.less
.root {
color: red;
}
// SomeComponent.tsx
import React, { FunctionComponent } from 'react';
import withStyles from 'isomorphic-style-loader/withStyles';
const SomeComponent: FunctionComponent<{
foo: string;
}> = ({ foo = 'this is red }) => <div className={s.root}>{foo}</div>;
export default withStyles(s)(SomeComponent);