-
Notifications
You must be signed in to change notification settings - Fork 324
Typescript support
As of version 2.0.0
stream-chat-react-native
has been converted to TypeScript. The stream-chat-js
library was converted to typescript in version 2.0.0
as well. These upgrades not only provide improved type safety but also allow through the use of generics for user provided typings to be passed to server responses, custom components, filters, etc.
In many cases TypeScript can use inference from a provided prop to infer the generics used. It is therefore important that the proper generics be applied to the stream-chat-js
client when it is instantiated. The documentation on stream-chat-js
TypeScript has examples of how this can be done in detail. The client takes seven optional generics that correspond to the seven customizable fields that currently exist in stream-chat-js
.
const client = new StreamChat<
AttachmentType,
ChannelType,
CommandType,
EventType,
MessageType,
ReactionType,
UserType
>('YOUR_API_KEY', 'API_KEY_SECRET');
The seven customizable fields these generics extend are provided via stream-chat-js
.
All seven generics contain defaults in the stream-chat-react-native
repo that you can extend for custom data. Additional fields on the defaults i.e. file_size
, mime_type
, and image
are custom fields used by stream-chat-react-native
already within the SDK. When wanting to set a subset of generics the preceding and interceding generics must also be set in order for the TypeScript compiler to correctly understand intent.
To set ChannelType
and MessageType
for instance the initialization would be:
const client = new StreamChat<
DefaultAttachmentType,
{ image?: string, nickName?: string },
DefaultCommandType,
DefaultEventType,
{ isAdminMessage?: boolean },
>('YOUR_API_KEY', 'API_KEY_SECRET');
Note: DefaultCommandType
extends string & {}
instead of string
to maintain intellisense for the included commands. In use a string union such as 'poll' | 'question'
could be used to extend them.
type DefaultAttachmentType = Record<string, unknown> & {
file_size?: number | string;
mime_type?: string;
};
type DefaultChannelType = Record<string, unknown> & {
image?: string;
};
type DefaultCommandType = string & {};
type DefaultEventType = Record<string, unknown>;
type DefaultMessageType = Record<string, unknown>;
type DefaultReactionType = Record<string, unknown>;
type DefaultUserType = Record<string, unknown> & {
image?: string;
};
The TypeScript Example App shows how to apply the generics to many of the components in the SDK. Core to understanding this usage is how generics can be applied to JSX elements.
In many cases the use of a single prop such as client
or channel
allows TypeScript to infer the generics on an element. In this case LocalAttachmentType
is inferred from channel
and passed to the props type for a custom Attachment component.
Not all components use or are always provided a prop that can provide inference though. In these cases the generics must be applied to the component directly. MessageList
for instance could have the previous generics applied to it.
<MessageList<
DefaultAttachmentType,
{ image?: string, nickName?: string },
DefaultCommandType,
DefaultEventType,
{ isAdminMessage?: boolean },
>
onThreadSelect={(thread) => {
setThread(thread);
if (channel?.id) {
navigation.navigate('Thread', { channelId: channel.id });
}
}}
/>
This passes the generics through appropriately to custom components and other props, in this case the custom Message
component would receive the generics.
The context hooks provided also require generics to be applied to correctly type custom returns. useChannelContext
for instance would have the previous generics applied to it to get a correctly typed return for channel
.
const { channel } = useChannelContext<
DefaultAttachmentType,
{ image?: string, nickName?: string },
DefaultCommandType,
DefaultEventType,
{ isAdminMessage?: boolean },
>();
Note: Inference only works correctly when all generics are provided by a given input. Partial Type Argument Inference is currently not supported in TypeScript. This is particularly relevant if the Higher Order Components are used in place of the provided context hooks. The withChannelContext
HOC accepts the generics similarly to the useChannelContext
hook, but because partial inference is not supported the props for the wrapped component must also be explicitly provided.
withChannelContext<
MyComponentProps,
DefaultAttachmentType,
{ image?: string, nickName?: string },
DefaultCommandType,
DefaultEventType,
{ isAdminMessage?: boolean },
>(MyComponent);