diff --git a/docs/custom-layouts.md b/docs/custom-layouts.md
index 8443758e..b26a6ba3 100644
--- a/docs/custom-layouts.md
+++ b/docs/custom-layouts.md
@@ -86,3 +86,59 @@ Toast.show({
```
All the available props on `BaseToast`, `SuccessToast`, `ErrorToast` or `InfoToast` components can be found here: [BaseToastProps](../src/types/index.ts#L86-L103).
+
+## Custom toast types with TypeScript
+
+When you create a custom toast type as in the example above, you can make it fully type-safe by utilizing Typescript declaration merging.
+
+Example:
+
+```tsx
+import Toast, { ToastConfig } from 'react-native-toast-message';
+
+interface TomatoToastParams {
+ uuid: string;
+ // ...
+}
+
+declare module 'react-native-toast-message' {
+ export interface CustomToastParamTypes {
+ tomatoToast: TomatoToastParams;
+ }
+}
+```
+
+Now the `props` parameter will be correctly typed according to the toast type.
+
+```tsx
+const toastConfig: ToastConfig = {
+ tomatoToast: ({ text1, props }) => (
+
+ {text1}
+ {/* `props` will be of type `TomatoToastParams` here */}
+ {props.uuid}
+
+ )
+};
+```
+Note that if you specify a custom toast type, then the config prop on the Toast component will become required:
+
+```tsx
+export function App(props) {
+ return (
+ <>
+ {...}
+ {/* Property 'config' is missing in type '{}' but required in type '{ config: ToastConfig; }'.ts(2741) */}
+ >
+ );
+}
+```
+
+Then you can use the new toast type with the correct typing:
+```ts
+Toast.show({
+ type: 'tomatoToast',
+ // if you mess something up in the props, typescript will scream at you properly
+ props: { uuid: 'bba1a7d0-6ab2-4a0a-a76e-ebbe05ae6d70' }
+});
+```
diff --git a/src/Toast.tsx b/src/Toast.tsx
index 2cc85c7c..2d65eebc 100644
--- a/src/Toast.tsx
+++ b/src/Toast.tsx
@@ -6,7 +6,8 @@ import {
ToastHideParams,
ToastProps,
ToastRef,
- ToastShowParams
+ ToastShowParams,
+ ToastType
} from './types';
import { useToast } from './useToast';
@@ -121,7 +122,7 @@ function getRef() {
return activeRef.current;
}
-Toast.show = (params: ToastShowParams) => {
+Toast.show = (params: ToastShowParams) => {
getRef()?.show(params);
};
diff --git a/src/types/index.ts b/src/types/index.ts
index 43cbda27..2809e198 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -10,15 +10,14 @@ import {
export type ReactChildren = React.ReactNode;
-export type ToastType = 'success' | 'error' | 'info' | (string & {});
export type ToastPosition = 'top' | 'bottom';
-export type ToastOptions = {
+export type ToastOptions = {
/**
* Toast type.
* Default value: `success`
*/
- type?: ToastType;
+ type?: T;
/**
* Style for the header text in the Toast (text1).
*/
@@ -85,20 +84,41 @@ export type ToastOptions = {
* Called on Toast press
*/
onPress?: () => void;
- /**
- * Any custom props passed to the specified Toast type.
- * Has effect only when there is a custom Toast type (configured via the `config` prop
- * on the Toast instance) that uses the `props` parameter
- */
- props?: any;
-};
+} & (keyof CustomToastParamTypes extends never
+ ? { props?: any }
+ : T extends keyof CustomToastParamTypes
+ ? Required[T] extends never
+ ? { props?: any }
+ : undefined extends CustomToastParamTypes[T]
+ ? {
+ /**
+ * Any custom props passed to the specified Toast type.
+ * Has effect only when there is a custom Toast type (configured via the `config` prop
+ * on the Toast instance) that uses the `props` parameter
+ */
+ props?: CustomToastParamTypes[T];
+ }
+ : {
+ /**
+ * Any custom props passed to the specified Toast type.
+ * Has effect only when there is a custom Toast type (configured via the `config` prop
+ * on the Toast instance) that uses the `props` parameter
+ */
+ props: CustomToastParamTypes[T];
+ }
+ : { props?: any });
export type ToastData = {
text1?: string;
text2?: string;
};
-export type ToastShowParams = ToastData & ToastOptions;
+export type ToastShow = (
+ params: ToastShowParams
+) => void;
+
+export type ToastShowParams = ToastData &
+ ToastOptions;
export type ToastHideParams = void;
@@ -135,12 +155,33 @@ export type ToastConfigParams = {
props: Props;
};
+/**
+ * This interface is here to allow for types customization via declaration merging when using custom toast types.
+ * See [the docs](https://github.com/calintamas/react-native-toast-message/blob/main/docs/custom-layouts.md) for usage examples.
+ */
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface CustomToastParamTypes {}
+
+export interface BuiltinToastParamsTypes {
+ success?: never;
+ error?: never;
+ info?: never;
+}
+
+export type ToastType = keyof (BuiltinToastParamsTypes & CustomToastParamTypes);
+
export type ToastConfig = {
- [key: string]: (params: ToastConfigParams) => React.ReactNode;
-};
+ [key in keyof BuiltinToastParamsTypes]?: (
+ params: ToastConfigParams
+ ) => React.ReactNode;
+} & {
+ [key in keyof CustomToastParamTypes]-?: (
+ params: ToastConfigParams
+ ) => React.ReactNode;
+} & Record) => React.ReactNode>;
export type ToastRef = {
- show: (params: ToastShowParams) => void;
+ show: ToastShow;
hide: (params: ToastHideParams) => void;
};
@@ -148,11 +189,19 @@ export type ToastRef = {
* `props` that can be set on the Toast instance.
* They act as defaults for all Toasts that are shown.
*/
-export type ToastProps = {
- /**
- * Layout configuration for custom Toast types
- */
- config?: ToastConfig;
+export type ToastProps = (keyof CustomToastParamTypes extends never
+ ? {
+ /**
+ * Layout configuration for custom Toast types
+ */
+ config?: ToastConfig;
+ }
+ : {
+ /**
+ * Layout configuration for custom Toast types
+ */
+ config: ToastConfig;
+ }) & {
/**
* Toast type.
* Default value: `success`