When we use styled-component in TypeScript to inherit the Material-UI component, props type does not match.
How do I overwrite a style with styled-component?
There is a sample on how to inherit in the case of styled-component in the following link, but this sample will generate an error if it is set to strict: true
in tsconfig setting.
https://material-ui.com/guides/interoperability/#styled-components
import React from 'react';
import styled from 'styled-components';
import Button from '@material-ui/core/Button';
const StyledButton = styled(Button)`
background: linear-gradient(45deg, #fe6b8b 30%, #ff8e53 90%);
border-radius: 3px;
border: 0;
color: white;
height: 48px;
padding: 0 30px;
box-shadow: 0 3px 5px 2px rgba(255, 105, 135, .3);
`;
function StyledComponentsButton() {
return (
<div>
<Button>
Material-UI
</Button>
<StyledButton>
Styled Components
</StyledButton>
</div>
);
}
export default StyledComponentsButton;
The way I tried is to cast the component with React.SFC and then pass the props type of the material-ui component to that generics.
import TextField, { TextFieldProps } from "@material-ui/core/TextField";
export const StyledTextField = styled(TextField as React.SFC<TextFieldProps>)`
// some style
`;
in this case
Type '{ disabled: true; label: string; defaultValue: string; variant: "outlined"; }' is missing the following properties from type 'Pick<Pick<TextFieldProps.....
The above error will be displayed :(
It was able to solve by the following method.
interface TextFieldProps {
disabled: boolean;
label: string;
defaultValue: string;
variant: string;
}
export const StyledTextField = styled(TextField as React.SFC<TextFieldProps>)`
// some style
`;
However, I do not think this is a nice implementation. Required Props
It is because you have to re-implement the interface every time it changes.
@material-ui/core version: 3.6.2
@material-ui/icons version: 3.0.1
styled-components version: 4.1.2
typescript version: 3.2.2
macOS: 10.13.6
There's some documentation on the TypeScript Guide page related to using type widening with createStyles
to overcome some of these issues: https://material-ui.com/guides/typescript/
Same issue here.
In case it's helpful file with issue is:
import { TextField } from '@material-ui/core';
import styled from '../../theme';
export const CustomSelect = styled(TextField)`
width: 100%;
max-width: 300px;
`;
And tsconfig is:
{
"compilerOptions": {
"allowJs": true,
"baseUrl": ".",
"forceConsistentCasingInFileNames": true,
"jsx": "react",
"lib": ["es6", "dom"],
"module": "esnext",
"moduleResolution": "node",
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"outDir": "build",
"paths": {
"src/*": ["src/*"],
"test/*": ["test/*"]
},
"rootDir": "src",
"sourceMap": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"target": "es5",
"typeRoots": ["src/typings", "node_modules/@types"],
},
"include": [
"src",
"test"
]
}
These problems can also be overridden by style unless it is strict: true
. If you check strictly against type, you will get an error.
This happens because interface is defined as follows, but you can also see the idea of the author.
export interface BaseTextFieldProps
extends StandardProps<FormControlProps, TextFieldClassKey, 'onChange' | 'defaultValue'> {
autoComplete?: string;
autoFocus?: boolean;
children?: React.ReactNode;
defaultValue?: string | number;
disabled?: boolean;
error?: boolean;
FormHelperTextProps?: Partial<FormHelperTextProps>;
fullWidth?: boolean;
helperText?: React.ReactNode;
id?: string;
InputLabelProps?: Partial<InputLabelProps>;
inputRef?: React.Ref<any> | React.RefObject<any>;
label?: React.ReactNode;
margin?: PropTypes.Margin;
multiline?: boolean;
name?: string;
onChange?: React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>;
placeholder?: string;
required?: boolean;
rows?: string | number;
rowsMax?: string | number;
select?: boolean;
SelectProps?: Partial<SelectProps>;
type?: string;
value?: Array<string | number | boolean> | string | number | boolean;
}
export interface StandardTextFieldProps extends BaseTextFieldProps {
variant?: 'standard';
InputProps?: Partial<StandardInputProps>;
inputProps?: StandardInputProps['inputProps'];
}
export interface FilledTextFieldProps extends BaseTextFieldProps {
variant: 'filled';
InputProps?: Partial<FilledInputProps>;
inputProps?: FilledInputProps['inputProps'];
}
export interface OutlinedTextFieldProps extends BaseTextFieldProps {
variant: 'outlined';
InputProps?: Partial<OutlinedInputProps>;
inputProps?: OutlinedInputProps['inputProps'];
}
export type TextFieldProps = StandardTextFieldProps | FilledTextFieldProps | OutlinedTextFieldProps;
So I solved this problem as follows.
Although this is not the best policy, I feel that it is more a matter of operation as a team rather than a material-ui problem any more.
Include the component in a specific directory that extends material - ui.
Taking TextField as an example
src/components/extensions/TextFiled
import * as React from "react";
import {
default as MUITextFiled,
StandardTextFieldProps,
FilledTextFieldProps,
OutlinedTextFieldProps
} from "@material-ui/core/TextField";
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
export const StandardTextField: React.ComponentType<Omit<StandardTextFieldProps, "variant">> = props => (
<MUITextFiled {...props} variant="standard" />
);
export const FilledTextField: React.ComponentType<Omit<FilledTextFieldProps, "variant">> = props => (
<MUITextFiled {...props} variant="filled" />
);
export const OutlinedTextField: React.ComponentType<Omit<OutlinedTextFieldProps, "variant">> = props => (
<MUITextFiled {...props} variant="outlined" />
);
From another program, we do not import import Material - UI 's TextField so we will import from this extensions
. This eliminates TypeScript type problems.
The base problem is with type unions i.e. Foo | Bar
and Omit
. I don't think we can do much here beyond using any
cast:
const StyledTextField: React.ComponentType<TextFieldProps> = styled(TextField)({}) as any;
Not sure if this defeats any type checking in styled
though.
@eps1lon Solution appears to work for me. Final result was
import TextField, { TextFieldProps } from '@material-ui/core/TextField';
export const StyledTextField: React.ComponentType<TextFieldProps> = styled(TextField)`
` as any;
Thanks!
I think this should be related to https://github.com/DefinitelyTyped/DefinitelyTyped/issues/29832 and possibly https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30942
I also suggest, that this issue is not only causing problems for strict: true
. In my typescript project it leaves material-ui completely incompatible to styled-components (after updating @types/styled-components to >=4.1.0). And all I use is strictNullChecks, noImplicitAny and noImplicitThis.
Every single component is affected.
e.g.
declare const Typography: React.ComponentType<TypographyProps>;
....
type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>;
Please correct me here if this is a different problem. I will open a new issue then.
I encourage you to use
const StyledButton = styled(Button)`` as typeof Button;
This will remove any type information for props added by styled-components
. However, most of these shouldn't be necessary anymore. Please let me know if there are any props from styled-components
that are essential in your opinion.
There are both issues with the styled-component types (union props are lost: changing this caused a 20x perf regression in ts 3.4) and TypeScript design limitations (argument inference only considers one function overload).
@eps1lon by changing to not losing union props you are meaning switching from using Pick
to using Pick
distributed over union with extends any
conditional clause? If so, can you provide an example of perf regression? I've monkey-patched my local styled-components definitions and as of TS 3.5 unable to notice any regression whatsoever — maybe TS team updated smth related in TS 3.5?
@radiosterne Sounded like it. The regression was dicovered in 3.4: https://github.com/microsoft/TypeScript/issues/30663
@eps1lon that's great; I take it we are now able to substitute Pick
for a better version, or do you mind? I'll make a pull request to DefinitelyTyped, if you're ok with it.
@radiosterne Have you done some progress on the topic?
Most helpful comment
I encourage you to use
This will remove any type information for props added by
styled-components
. However, most of these shouldn't be necessary anymore. Please let me know if there are any props fromstyled-components
that are essential in your opinion.There are both issues with the styled-component types (union props are lost: changing this caused a 20x perf regression in ts 3.4) and TypeScript design limitations (argument inference only considers one function overload).