Add more of the new features added in React 16.6#30054
Add more of the new features added in React 16.6#30054johnnyreilly merged 21 commits intoDefinitelyTyped:masterfrom
Conversation
d0cdd32 to
19c6468
Compare
| /** | ||
| * Not implemented yet, requires unstable_ConcurrentMode | ||
| */ | ||
| // maxDuration?: number |
There was a problem hiding this comment.
I don't know whether to leave this in or not. The code to support it technically exists, but is unreachable without unstable_ConcurrentMode which we are not defining here.
| * @deprecated | ||
| * https://reactjs.org/docs/legacy-context.html | ||
| */ | ||
| context: any; |
There was a problem hiding this comment.
I removed this to make sure there's no magic any in this.context. That'll only happen when using legacy context; if using modern context it'll be the type of the value prop of this.constructor.contextType.Provider.
There was a problem hiding this comment.
Hm, it is strange, cause here https://reactjs.org/docs/context.html#classcontexttype we can see, React allows us to use just this.context, but it is not possible, if context will be deleted from types of Component.
There was a problem hiding this comment.
By the way, there is no anything like value in Component.contextType.Provider.
There was a problem hiding this comment.
The point is to force the user to declare it themselves because there is nothing better we can do other than any here, and having it already declared would allow people to access it even without a static contextType.
value is the value prop of the Provider exotic component. The actual type of context would be something like typeof this.constructor.contextType extends React.Context<infer T> ? T : never if this.constructor would be useful (its typeof is just Function).
There was a problem hiding this comment.
Hm, could you explain, why this code is not working:
class Component<P, S> {
static contextType: Context<any>;
...
...
context: typeof Component.contextType extends Context<infer T> ? T : never
}
Type of context is any always( But it works perfectly in my own component.
There was a problem hiding this comment.
That is because, by declaring it in the superclass, you don't have access to its type as assigned in the subclass. The type of the constructor itself is not generic so it just uses the type as declared, assuming Component is constructed as is and not through a subclass. (Note: despite how I phrased it, this still wouldn't work if Component was abstract)
This is the deficiency in TypeScript's class declaration language: it is possible to make an instance type generic, but not the constructor object itself generic.
There is a possible hack with constructible interfaces, though, if they're even accepted in an extends clause. It could be possible to just completely replace the class Component declaration with a pair of interfaces and a const. But... at what cost?.
There was a problem hiding this comment.
Thank you for explanation. I agree, what it is ok to get rid of context from typings.
| * | ||
| * Note: its presence prevents any of the deprecated lifecycle methods from being invoked | ||
| */ | ||
| (error: any) => Partial<S> | null; |
There was a problem hiding this comment.
I actually don't know if this is allowed to return null 🤔
There was a problem hiding this comment.
Should the argument be of type Error?
| (error: any) => Partial<S> | null; | |
| (error: Error) => Partial<S> | null; |
There was a problem hiding this comment.
I kept it the same type as catch has.
try {
...
} catch (e) {
// e will _always_ be `any`
// giving it another type is a build error
}You can also throw anything in javascript; even throw undefined.
| ): MemoExoticComponent<T>; | ||
|
|
||
| interface LazyExoticComponent<T extends ComponentType<any>> extends ExoticComponent<ComponentPropsWithRef<T>> { | ||
| readonly _result: T; |
There was a problem hiding this comment.
I'm not really happy about this. Having an underscored value here looks like reaching into internals; but it's the only way to have access to T in LibraryManagedProps.
An alternative is to make up our own fictional prop name.
| // fnc: (() => 'abc') as () => any, | ||
| // extraBool: false, | ||
| // reqNode: 'text_node' as React.ReactNode | ||
| // reqNode: 'text_node' as NonNullable<React.ReactNode> |
There was a problem hiding this comment.
I fixed lints that were hit while I was editing this uncommented.
| // // $ExpectType FunctionalComponentLibraryManagedAttributes | ||
| // type MemoFunctionalComponentLibraryManagedAttributes = JSX.LibraryManagedAttributes<typeof MemoFunctionalComponent, Props>; | ||
| // // $ExpectType FunctionalComponentLibraryManagedAttributes | ||
| // type LazyMemoFunctionalComponentLibraryManagedAttributes = JSX.LibraryManagedAttributes<typeof LazyMemoFunctionalComponent, Props>; |
There was a problem hiding this comment.
Assuming I'm using $ExpectType correctly these all pass. It just doesn't actually work when the functional/exotic components are used in JSX.
| <LazyClassComponent hello='test'/> | ||
| <LazyClassComponent ref={ref => { if (ref) { ref.props.hello; } }} hello='test'/> | ||
| <LazyMemoized3 ref={ref => { if (ref) { ref.props.x; } }}/> | ||
| <LazyRefForwarding ref={memoized4Ref}/> |
There was a problem hiding this comment.
I couldn't figure out how refs work by reading mountLazyComponent in ReactFiberBeginWork.js; I actually thought they didn't work at all at first.
I did verify that they work at runtime, though (refs are set/called when the Suspense resolves).
|
@Kovensky Thank you for submitting this PR! 🔔 @johnnyreilly @bbenezech @pzavolinsky @digiguru @ericanderson @morcerf @tkrotoff @DovydasNavickas @onigoetz @theruther4d @guilhermehubner @ferdaber @jrakotoharisoa @pascaloliv @Hotell @franklixuefei @kaoDev @guntherjh @wasd171 @szabolcsx @kraenhansen @Stevearzh @mgoszcz2 @brandonhall @eps1lon - please review this PR in the next few days. Be sure to explicitly select If no reviewer appears after a week, a DefinitelyTyped maintainer will review the PR instead. |
|
@Kovensky The Travis CI build failed! Please review the logs for more information. Once you've pushed the fixes, the build will automatically re-run. Thanks! |
19c6468 to
3efef9d
Compare
|
@Kovensky The Travis CI build failed! Please review the logs for more information. Once you've pushed the fixes, the build will automatically re-run. Thanks! |
3efef9d to
c1e70d4
Compare
|
@Kovensky The Travis CI build failed! Please review the logs for more information. Once you've pushed the fixes, the build will automatically re-run. Thanks! |
793cea3 to
a8bd490
Compare
|
@Kovensky The Travis CI build failed! Please review the logs for more information. Once you've pushed the fixes, the build will automatically re-run. Thanks! |
| children?: ReactNode | ||
|
|
||
| /** A fallback react tree to show when a Suspense child (like React.lazy) suspends */ | ||
| fallback: NonNullable<ReactNode>|null |
There was a problem hiding this comment.
React will throw at runtime if the Suspense suspends and the fallback is undefined, but null is ok.
mikiest
left a comment
There was a problem hiding this comment.
Nice one!
Just a couple of minor comments about possibly avoidable anys.
Also, is it worth cleaning up some comments before merging?
| * | ||
| * Note: its presence prevents any of the deprecated lifecycle methods from being invoked | ||
| */ | ||
| (error: any) => Partial<S> | null; |
There was a problem hiding this comment.
Should the argument be of type Error?
| (error: any) => Partial<S> | null; | |
| (error: Error) => Partial<S> | null; |
|
|
||
| function forwardRef<T, P = {}>(Component: RefForwardingComponent<T, P>): ForwardRefExoticComponent<P & ClassAttributes<T>>; | ||
|
|
||
| type ComponentProps<T extends ComponentType<any>> = |
There was a problem hiding this comment.
Do we need <any> here (and in the few other instances below)?
It looks like ComponentType generic will default to {} anyway, so maybe you could omit it?
See here:
DefinitelyTyped/types/react/index.d.ts
Line 53 in 549360d
There was a problem hiding this comment.
Yeah, if there is no argument it'll be inferred as {} and this will actually cause the compiler to reject subtypes of ComponentType<{}> because {} will be a function or constructor argument's type.
It's the problem of (arg: { foo: string }) => null not being assignable to (arg: {}) => null, but being assignable to (arg: any) => null.
|
@Kovensky I have used the following React.lazy definition for a while in my types.d.ts: interface ModuleLike<T> {
default: T;
}
import * as React from 'react';
declare module 'react' {
/**
* Otherwise works, but 'show all usages' work only for prop names, not for component names, unfortunately
*/
function lazy<T>(f: () => T): T extends Promise<infer U> ?
(U extends ModuleLike<infer V> ? V : never) :
never;
} |
…ds to prevent that
Hopefully this trips tslint's deprecation rule.
…at should support them This doesn't actually work because it seems only class components get them checked?
This also attempts to add support for LibraryManagedAttributes to the ExoticComponents.
6723530 to
3a26c43
Compare
|
Rebased 🔥 |
|
Nice! Just wait for ci to finish and we'll get this in too! |
|
Done! |
|
if @types/react has version 16.7.1 in npm then it should contain things from react 16.7.0-alpha.0 like maxDuration in React.Suspense witch is now commented out, what do you think? when Suspense is defined as const const Suspense: ExoticComponent<{
children?: ReactNode;
/** A fallback react tree to show when a Suspense child (like React.lazy) suspends */
fallback: NonNullable<ReactNode> | null;
// I tried looking at the code but I have no idea what it does.
// https://github.com/facebook/react/issues/13206#issuecomment-432489986
/**
* Not implemented yet, requires unstable_ConcurrentMode
*/
// maxDuration?: number;
}>;its imposible to overwrite it, and make maxDuration visible. making props as an interface would not prevent me to use interface declaration merging, when I want to add maxDuration in my project |
| } | ||
|
|
||
| function memo<P extends object>( | ||
| Component: SFC<P>, |
There was a problem hiding this comment.
Is this right? If I have a render prop component (where children might be a function), it's not being inferred as functional component.
There was a problem hiding this comment.
Implicit children strike back
Again
🙄
I guess I have a New Years break project for a breaking change. Hopefully one I can get to compile.
There was a problem hiding this comment.
I'm investigating it further. The problem may not be children, but something else (my current bet is functional components with custom generic types). I'll see if I can come up with a minimum reproduction.
There was a problem hiding this comment.
@sandersn @johnnyreilly Would it be possible to condition this breaking change on a specific TypeScript version through typesVersion?
I was thinking of being able to make the change incremental by only updating types on demand, when people update to, say, TS3.3. This would require kind of a "max supported TS version" on types that depend on @types/react.
I'm a bit afraid of it being a python3 problem, though, but the technical debt of @types/react is now nigh-unmanageable.
Yes, I could work around this problem in this specific instance by changing the input to a function instead of FunctionComponent, but it's pervasive and everywhere. A lot of things that currently work in the ecosystem (like function children) work because of ossified bugs in the types, not because of features. @types/react is due for a rewrite.
There was a problem hiding this comment.
@diegohaz ah, yes, that is the one big problem with mapped types and infer (and the lack of kinds?); they erase generics and collapse unions. If the props are either of those the output type may be funky.
There was a problem hiding this comment.
In another thread we decided it was time to update the types to expect TS 3
There was a problem hiding this comment.
Would it be possible to condition this breaking change on a specific TypeScript version through typesVersion?
I'm afraid I haven't played with typesVersion yet and so can't advise. That said, as @ericanderson mentioned there's been prior discussion about upgrading to TS 3. We should do that anyway. If anyone is thinking about submitting a PR that does that then they should expect


Continues off #29990.
npm test.)npm run lint package-name(ortscif notslint.jsonis present).If changing an existing definition: