If you know how to fix the issue, make a pull request instead.
@types/react package and had problems.Definitions by: in index.d.ts) so they can respond.If you do not mention the authors the issue will be ignored.
Hello! Thank you in advance for your help.
I ran into an issue where defaultProps is not type-checked against the props generic for React components.
Here is a simple example component that illustrates the issue.
import React from 'react';
type Props = { foo: string };
class Foo extends React.Component<Props> {
static defaultProps = {
foo: 123,
};
render() {
return <div />;
}
}
Here, we can see the type expected from the consumer (string | undefined)

Would have expected an error to come up because the number foo from defaultProps is not assignable to string from Props. Instead, I see no error. The default prop is picked up to make the foo prop optional, but appears that the type information is ignored.
Looks like this is where the defaultizing happens
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a95857f87476f9a30085c3728530ad10586ae1e4/types/react/index.d.ts#L2316
Which only looks at the keys of the defaultProps
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a95857f87476f9a30085c3728530ad10586ae1e4/types/react/index.d.ts#L2290-L2299
Would have expected Partial<P> to have resulted in some checking
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a95857f87476f9a30085c3728530ad10586ae1e4/types/react/index.d.ts#L363
const dp: typeof Foo['defaultProps'] = { foo: 1 };
const pp: Partial<Props> = dp;

Why is there no error when the static defaultProps for a React component is not assignable to Partial<P>?
Would have expected
Partial<P>to have resulted in some checking
This declaration is only in React.ComponentClass, so it only comes into play when a component class is used in a context that expects it to be a React.ComponentClass (or React.ComponentType), such as passing it to a higher-order component. If you just define a subclass of React.Component and use it in a JSX element, nothing on that compiler code path checks the defaultProps.
We could easily change JSX.LibraryManagedAttributes to report an error if the defaultProps are incompatible with the props type, but it would be much better to report an error on the class definition. Unfortunately, I'm not aware of any TypeScript feature that we could use to do this without extra boilerplate in each component, and I'm not sure it would be reasonable to ask the TypeScript team to add a feature just to accommodate the defaultProps design that the React team chose without any consideration of TypeScript typability. (Yes, I know TypeScript added JSX specifically to accommodate React and similar libraries, but I hope we can keep perspective that while validating defaultProps is important, it isn't in the same league of priority as supporting JSX in the first place.)
The cleanest solution I can find that works in current TypeScript is to define a helper function that provides the contextual type:
import * as React from "react";
// Note: `prototype` should do what we want for generic components (FBOFW).
function asDefaultProps<P>(component: {prototype: {props: P}}) {
return <DP extends Partial<P>>(defaultProps: DP) => defaultProps;
}
class MyComponent extends React.Component<{x: (y: string) => number}, {}> {
static defaultProps = asDefaultProps(MyComponent)({
x: (y) => 42
});
}
Of course, developers would have to remember to use the function. One way to enforce that would be to have asDefaultProps add a brand to its result and add to class React.Component:
static defaultProps?: DefaultProps;
where DefaultProps is an interface that is empty by default but can be augmented on a per-compilation basis to require the presence of the brand.
Thoughts about what we should do?
Aside: #16967 is related but seems unlikely to result in a solution for defaultProps.
I think one fix would be to annotate the static defaultProps as Partial<Props>, does that work?
I think one fix would be to annotate the static
defaultPropsasPartial<Props>, does that work?
Do you mean in each component? If so, no, because then JSX.LibraryManagedAttributes will see the type as Partial<Props>. We need it to see which props are actually present and which are not.
Ah derp, yep, there would need to be a Pick which would be a huge hassle. One issue that I ran into during the implementation is that sometimes a prop would be a union of literal types buttonType: 'big' | 'medium' | 'small', and when creating default props, because it's an object literal, the type of its properties are widened (buttonType: 'big' has type string) which fails.
This I remember was why I removed the type checking from default props.
OK... What do you think of my proposed solution then? Since asDefaultProps has to be defined at runtime, it couldn't be put in @types/react but would have to be put in a tiny separate npm package and then officially recommended by the @types/react documentation.
I'm not sure how we would add the recommendation in a place where people will be able to see it (maybe in the comments of library managed attributes, or defaultize?), but yeah there are tons of npm packages out there that performs helper operations on types like this, and I don't see any other way that we'd be able to type check a static property while also preserving the 'partialness' of default props.
The natural place is to declare static properties defaultProps and propTypes of type any and put the recommendation in JSDoc comments on those properties. Here is the draft change to @types/react and the draft implementation of the helpers.
In the test case for asPropTypes, I am getting the following error:
Argument of type '{ name: Requireable<string>; }' is not assignable to parameter of type 'ValidationMap<{ name: string; }>'.
Types of property 'name' are incompatible.
Type 'Requireable<string>' is not assignable to type 'Validator<string>'.
Types of property '[nominalTypeHack]' are incompatible.
Type 'string | null | undefined' is not assignable to type 'string | undefined'.
Type 'null' is not assignable to type 'string | undefined'.
I guess no one has tried this before. @ferdaber, do you understand what the problem is and how it should be fixed? I was missing isRequired.
all you need for now is described here 馃憠https://medium.com/@martin_hotell/react-typescript-and-defaultprops-dilemma-ca7f81c661c7
and open sourced 馃憠https://github.com/Hotell/rex-tils
@Hotell thanks for putting so much thought in the "million dollar problem", writing that article and creating rex-tils, probably helping lots of TS/React users and improving their DX.
Unfortunately there're two flaws in your otherwise great solutions. First it introduced some boilerplate (e.g. defining defaultProps and its values in an own object outside of the class, calling a some function, declaring a special type, etc.). The second, and probably more precarious one is, that since TypeScript 3.0 (maybe even before that) you don't have to explicitly declare properties as "optional" (adding ?, marking as "can be undefined"), instead TS will infer this by checking the defaultProps object and marking all class props with a corresponding prop in defaultProps as optional. I think this is pretty valuable and might even lead me to banning the ? from class props interfaces all together.
@mattmccutchen I really like your asDefaultProps solution! It only leaves a small "footprint" in the code and you get all the desired features (i.e. type checking of defaultProps, all props becoming optional which have a defaultProp, etc.).
On question though: the current declaration of @types/react allows for defaultProps which aren't included in the actual props interface (see this comment). I can't think of a use case for that and as a TypeScript user I find it misleading (and it also won't prevent you form typos in the defaultProps object). Question is: might this have been by design? Because your solution wouldn't allow those undeclared props.
Thanks to both of you!
Thank you so much for these ideas! Was hoping to come up with a solution that introduces no runtime overhead / no code (aside from the type annotations, of course) that exists purely to satisfy the TypeScript compiler. My team is trying to stick to the principle that it's the same JS we would have written otherwise, just with the addition of type annotations. Wonder if there's a way to bake a check of whether defaultProps is compatible with P into JSX.LibraryManagedAttributes somehow...
Was hoping to come up with a solution that introduces no runtime overhead / no code (aside from the type annotations, of course) that exists purely to satisfy the TypeScript compiler. My team is trying to stick to the principle that it's the same JS we would have written otherwise, just with the addition of type annotations.
I don't buy that principle at all. Until someone presents profile data showing otherwise, I'm asserting that the runtime overhead of this kind of thing is negligible in the context of other optimizations that could potentially be made if one cared enough. Re code, of course we want to do the best we can, but you've presumably chosen to use TypeScript rather than JavaScript and pay the cost of longer code because you found the static checking and/or editor services beneficial, and I don't see how adding a piece of code that happens to be a runtime helper call rather than a type annotation significantly changes that calculus.
With that out of the way, as I said before, checking the defaultProps as part of JSX.LibraryManagedAttributes would be straightforward, but reporting the error on the definition of defaultProps rather than the call to the component gives a superior developer experience. The real question is whether that superior experience justifies the use of the helper call. I feel that it does, but we could additionally include the JSX.LibraryManagedAttributes check as a backstop in case the developer forgets the helper call. Other opinions?
That principle seems inline with some of the design goals of the language.
https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
Impose no runtime overhead on emitted programs.
Use a fully erasable type system.
That aside, I like your idea of adding a check to JSX.LibraryManagedAttributes as a types-only option, even though we wouldn't get the error in the most ideal place. And, people can opt-in to the separate helper call that would have better devx.
That principle seems inline with some of the design goals of the language.
FWIW to debate this here: I can see how adhering to that design goal might be useful to TypeScript: it helps focus TypeScript on delivering one set of functionality while pushing other functionality to separate libraries, and it helps ensure that TypeScript offers a different point in the design space from other compilers that do runtime checking. Whether a given library should alter its structure at runtime to make type checking work better is a completely different question, and it seems that based on benefits and costs, the answer is sometimes going to be yes.
I just wanted to note that the new TypeScript 3.0 feature for defaultProps (mentioned in Microsoft's blog post) is available and works as explained in the blog post, as of @types/react version 16.4.18. No need to do anything fancy unless you find the default inferred type of defaultProps to be insufficient for your needs.
@mbrowne yeah, that TS feature is available in that you won't need to provide a prop if there's an corresponding entry in the defaultProps object.
The problem we're trying to solve is, that tsc won't fail if you provide a value in the defaultProps object whose type doesn't match the prop declaration (see good example in @brieb's issue description).
But since I'm already here: @mattmccutchen how should we go about this? Do you suggest just using your helper until maybe React, TypeScript, or atleast the React types will come up with a better solution?
I've now published an initial version of the react-typescript-helpers package (I won't be surprised if problems are found that require a new version) and submitted pull request #30730, which adds the recommendation of react-typescript-helpers and the check in JSX.LibraryManagedAttributes.
Update: On #30730, @Hotell has proposed rejecting that pull request in favor of a different code pattern for defaultProps. Would the people supporting this issue please see if they are satisfied with his solution and add their feedback to #30730.
I am using @types/react^16.7.17, defaultProps is supported. Seems like @types/react than 16.4.6 already solves the problem.
Here is the link I got the answer.
https://stackoverflow.com/questions/37282159/default-property-value-in-react-component-using-typescript
but it didn't fix totally when I using it as a class.
when it is not an optional prop, and this prop has defaultProps.
such as:
class A<{a: string}>{
public static defaultProps = {
a: "something",
}
}
const foo = new A({})
not working, still need to supply prop a;
Most helpful comment
The natural place is to declare static properties
defaultPropsandpropTypesof typeanyand put the recommendation in JSDoc comments on those properties. Here is the draft change to@types/reactand the draft implementation of the helpers.In the test case forasPropTypes, I am getting the following error:I guess no one has tried this before. @ferdaber, do you understand what the problem is and how it should be fixed?I was missingisRequired.