-
-
Notifications
You must be signed in to change notification settings - Fork 651
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Typesafe data
- TypeScript
#935
Comments
Yes! Wanted to second this and bump it up... not a typescript pro but something like this on the consumer side I think is what we're looking for const { } = useDraggable<DataProps>({ id, data: { name } }); or const { } = useDraggable<{data: DataProps}>({ id, data: { name } }); and some way to cast it when consuming it... <DndContext
onDragStart={({ active }: {active: Active<DataProps>}) => {
//...
}}
> or again... <DndContext
onDragStart={({ active }: {active: Active<{data: DataProps}>}) => {
//...
}}
> Open to people's thoughts... |
For my case I simply wrap the hooks and components in a stricter type, and import and use that stricter version instead import {
Active,
Collision,
DndContextProps,
DndContext as OriginalDndContext,
Over,
Translate,
UseDraggableArguments,
UseDroppableArguments,
useDraggable as useOriginalDraggable,
useDroppable as useOriginalDroppable,
} from "@dnd-kit/core";
import { Element } from "slate";
interface DroppableData {
id: string;
position: string;
}
interface UseDroppableTypesafeArguments extends Omit<UseDroppableArguments, "data"> {
data: DroppableData;
}
export function useDroppable(props: UseDroppableTypesafeArguments) {
return useOriginalDroppable(props);
}
interface DraggableData {
element: Element;
}
interface UseDraggableTypesafeArguments extends Omit<UseDraggableArguments, "data"> {
data: DraggableData;
}
export function useDraggable(props: UseDraggableTypesafeArguments) {
return useOriginalDraggable(props);
}
interface TypesafeActive extends Omit<Active, "data"> {
data: React.MutableRefObject<DraggableData | undefined>;
}
interface TypesafeOver extends Omit<Over, "data"> {
data: React.MutableRefObject<DroppableData | undefined>;
}
interface DragEvent {
activatorEvent: Event;
active: TypesafeActive;
collisions: Collision[] | null;
delta: Translate;
over: TypesafeOver | null;
}
export interface DragStartEvent extends Pick<DragEvent, "active"> {}
export interface DragMoveEvent extends DragEvent {}
export interface DragOverEvent extends DragMoveEvent {}
export interface DragEndEvent extends DragEvent {}
export interface DragCancelEvent extends DragEndEvent {}
export interface DndContextTypesafeProps
extends Omit<
DndContextProps,
"onDragStart" | "onDragMove" | "onDragOver" | "onDragEnd" | "onDragCancel"
> {
onDragStart?(event: DragStartEvent): void;
onDragMove?(event: DragMoveEvent): void;
onDragOver?(event: DragOverEvent): void;
onDragEnd?(event: DragEndEvent): void;
onDragCancel?(event: DragCancelEvent): void;
}
export function DndContext(props: DndContextTypesafeProps) {
return <OriginalDndContext {...props} />;
} |
Thanks @joulev We want the hooks to return typesafe // useDroppable()
type UseDroppableTypesafeReturnValue = Omit<
ReturnType<typeof useOriginalDroppable>,
'active' | 'over'
> & {
active: TypesafeActive | null;
over: TypesafeOver | null;
};
export function useDroppable(props: UseDroppableTypesafeArguments) {
return useOriginalDroppable(props) as UseDroppableTypesafeReturnValue;
}
// useDraggable()
type UseDraggableTypesafeReturnValue = Omit<
ReturnType<typeof useOriginalDraggable>,
'active' | 'over'
> & {
active: TypesafeActive | null;
over: TypesafeOver | null;
};
export function useDraggable(props: UseDraggableTypesafeArguments) {
return useOriginalDraggable(props) as UseDraggableTypesafeReturnValue;
} |
another convenient way to patch types: import {
Active,
CancelDrop,
Collision,
CollisionDetection,
DndContext as OriginalDndContext,
DndContextProps,
DragCancelEvent,
DragEndEvent,
DragMoveEvent,
DragOverEvent,
DragStartEvent,
DroppableContainer,
Over,
useDndMonitor as baseUseDndMonitor,
useDraggable as baseUseDraggable,
UseDraggableArguments,
useDroppable as baseUseDroppable,
UseDroppableArguments,
} from "@dnd-kit/core";
export const typedDnd = <DragData, DropData>() => {
type TypesafeActive = Omit<Active, "data"> & {
data: React.MutableRefObject<DragData | undefined>;
};
type TypesafeOver = Omit<Over, "data"> & {
data: React.MutableRefObject<DropData | undefined>;
};
type ContextProps = Omit<
DndContextProps,
| "onDragStart"
| "onDragMove"
| "onDragOver"
| "onDragEnd"
| "onDragCancel"
| "cancelDrop"
| "collisionDetection"
> & {
onDragStart?: (
e: Omit<DragStartEvent, "active"> & {
active: TypesafeActive;
},
) => void;
onDragMove?: (
e: Omit<DragMoveEvent, "active" | "over"> & {
active: TypesafeActive;
over: TypesafeOver | null;
},
) => void;
onDragOver?: (
e: Omit<DragOverEvent, "active" | "over"> & {
active: TypesafeActive;
over: TypesafeOver | null;
},
) => void;
onDragEnd?: (
e: Omit<DragEndEvent, "active" | "over"> & {
active: TypesafeActive;
over: TypesafeOver | null;
},
) => void;
onDragCancel?: (
e: Omit<DragCancelEvent, "active" | "over"> & {
active: TypesafeActive;
over: TypesafeOver | null;
},
) => void;
cancelDrop?: (
e: Omit<Parameters<CancelDrop>[0], "active" | "over"> & {
active: TypesafeActive;
over: TypesafeOver | null;
},
) => ReturnType<CancelDrop>;
collisionDetection?: (
e: Omit<
Parameters<CollisionDetection>[0],
"active" | "droppableContainers"
> & {
active: TypesafeActive;
droppableContainers: Array<
Omit<DroppableContainer, "data"> & TypesafeOver
>;
},
) => Array<Omit<Collision, "data"> & TypesafeOver>;
};
const DndContext: React.NamedExoticComponent<ContextProps> =
OriginalDndContext as any;
const useDndMonitor: (
args: Pick<
ContextProps,
"onDragStart" | "onDragMove" | "onDragOver" | "onDragEnd" | "onDragCancel"
>,
) => void = baseUseDndMonitor as any;
const useDraggable: (
args: Omit<UseDraggableArguments, "data"> & { data: DragData },
) => Omit<ReturnType<typeof baseUseDraggable>, "active" | "over"> & {
active: TypesafeActive | null;
over: TypesafeOver | null;
} = baseUseDraggable as any;
const useDroppable: (
args: Omit<UseDroppableArguments, "data"> & { data: DropData },
) => Omit<ReturnType<typeof baseUseDroppable>, "active" | "over"> & {
active: TypesafeActive | null;
over: TypesafeOver | null;
} = baseUseDroppable as any;
return { DndContext, useDndMonitor, useDraggable, useDroppable };
}; then, you can specify a contract and use the hooks and context like this: import { typedDnd } from '@/lib/typedDnd'
type DragEvent = { card: string };
type DropEvent = { slot: number };
// fully typed!
const { DndContext, useDraggable, useDroppable, useDndMonitor } = typedDnd<
DragEvent,
DropEvent
>(); |
The
data
property is great for handling logic. However, it doesn't seem to be typesafe.It would be nice if the
DndContext
& thehooks
could be generic in order to keep total control of the typing.Currently I'm using the optional chain operator everywhere
?.
I need to handle this data.The text was updated successfully, but these errors were encountered: