Add typings for create-subscription

This commit is contained in:
Vincent Siao
2018-05-23 19:43:54 -07:00
parent bbca5a777b
commit da4e117346
4 changed files with 188 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
import * as React from "react";
import { createSubscription, Subscription } from "create-subscription";
//
// Example: Subscribing to event dispatchers
// https://github.com/facebook/react/blob/fa7fa812c70084e139d13437fb204fcdf9152299/packages/create-subscription/README.md#subscribing-to-event-dispatchers
// ----------------------------------------------------------------------------
// Start with a simple component.
// In this case, it's a functional component, but it could have been a class.
function FollowerComponent({ followersCount }: { followersCount: number }) {
return <div>You have {followersCount} followers!</div>;
}
interface EventDispatcher<T> {
value: T;
addEventListener(eventName: "change", onChange: (newValue: T) => any): void;
removeEventListener(eventName: "change", onChange: (newValue: T) => any): void;
}
// Create a wrapper component to manage the subscription.
// $ExpectType Subscription<EventDispatcher<number>, number>
const EventHandlerSubscription = createSubscription({
getCurrentValue: (eventDispatcher: EventDispatcher<number>) => eventDispatcher.value,
subscribe: (eventDispatcher: EventDispatcher<number>, callback) => {
const onChange = (event: any) => callback(eventDispatcher.value);
eventDispatcher.addEventListener("change", onChange);
return () => eventDispatcher.removeEventListener("change", onChange);
}
});
declare const eventDispatcher: EventDispatcher<number>;
// Your component can now be used as shown below.
// In this example, 'eventDispatcher' represents a generic event dispatcher.
<EventHandlerSubscription source={eventDispatcher}>
{value => {
// $ExpectType number
const followersCount = value;
return <FollowerComponent followersCount={followersCount} />;
}}
</EventHandlerSubscription>;
//
// Example: Subscribing to event dispatchers
// https://github.com/facebook/react/blob/fa7fa812c70084e139d13437fb204fcdf9152299/packages/create-subscription/README.md#subscribing-to-a-promise
// ----------------------------------------------------------------------------
// Start with a simple component.
const LoadingComponent: React.SFC<{ loadingStatus: string | undefined }> = ({ loadingStatus }) => {
if (loadingStatus === undefined) {
// Loading
} else if (loadingStatus === null) {
// Error
} else {
// Success
}
return null;
};
// Wrap the functional component with a subscriber HOC.
// This HOC will manage subscriptions and pass values to the decorated component.
// It will add and remove subscriptions in an async-safe way when props change.
// $ExpectType Subscription<Promise<string>, string | undefined>
const PromiseSubscription = createSubscription({
getCurrentValue: promise => {
// There is no way to synchronously read a Promise's value,
// So this method should return undefined.
return undefined;
},
subscribe: (promise: Promise<string>, callback: (newValue: string | undefined) => void) => {
promise.then(
// Success
callback,
// Failure
() => callback(undefined)
);
// There is no way to "unsubscribe" from a Promise.
// create-subscription will still prevent stale values from rendering.
return () => {};
}
});
declare const loadingPromise: Promise<string>;
// Your component can now be used as shown below.
<PromiseSubscription source={loadingPromise}>
{value => {
// $ExpectType string | undefined
const loadingStatus = value;
return <LoadingComponent loadingStatus={loadingStatus} />;
}}
</PromiseSubscription>;
//
// Error cases
// ----------------------------------------------------------------------------
declare const wrongPromise: Promise<number>;
// $ExpectError
<PromiseSubscription source={wrongPromise}>
{value => null}
</PromiseSubscription>;
// $ExpectError
const MismatchSubscription = createSubscription({
getCurrentValue: (a: number) => null,
subscribe: (a: string, callback) => (() => undefined)
});
// $ExpectError
const NoUnsubscribe = createSubscription({
getCurrentValue: (a: number) => a,
subscribe: (a: number, callback) => {
// oops, should've returned a callback here
}
});

44
types/create-subscription/index.d.ts vendored Normal file
View File

@@ -0,0 +1,44 @@
// Type definitions for create-subscription 16.4
// Project: https://github.com/facebook/react/tree/master/packages/create-subscription
// Definitions by: Asana <https://github.com/Asana>
// Vincent Siao <https://github.com/vsiao>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
import * as React from "react";
export interface SubscriptionProps<S, T> {
children: (value: T) => React.ReactNode;
source: S;
}
export interface SubscriptionState<S, T> {
source: S;
value: T;
}
export interface Subscription<S, T> extends React.ComponentClass<SubscriptionProps<S, T>> {}
export type Unsubscribe = () => any;
export interface SubscriptionConfig<S, T> {
/**
* Synchronously gets the value for the subscribed property.
* Return undefined if the subscribable value is undefined,
* Or does not support synchronous reading (e.g. native Promise).
*/
getCurrentValue: (source: S) => T;
/**
* Set up a subscription for the subscribable value in props, and return an unsubscribe function.
* Return false to indicate the property cannot be unsubscribed from (e.g. native Promises).
* Due to the variety of change event types, subscribers should provide their own handlers.
* Those handlers should not attempt to update state though;
* They should call the callback() instead when a subscription changes.
*/
subscribe: (
source: S,
callback: (newValue: T) => void
) => Unsubscribe;
}
export function createSubscription<S, T>(config: SubscriptionConfig<S, T>): Subscription<S, T>;

View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"jsx": "react",
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"create-subscription-tests.tsx"
]
}

View File

@@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }