Skip to content
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

DRAFT: Ts migration #640

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open

DRAFT: Ts migration #640

wants to merge 8 commits into from

Conversation

taylorpwhipp
Copy link
Contributor

This PR is a rough draft/ first step at updating our documentation----> typescript. I will probably need to iterate on this after review since this is my first experience with TS and it touched a lot of files/ code.

Copy link
Collaborator

@nvie nvie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Taylor, this is a great first draft! I did a review pass for a bunch of updated examples in those MDX files (but not all of them yet). All of those changes look good to me structurally. The comments I left focused mostly on TypeScript best practices, but none of them are anything major and should be easy to fix. Structurally, these changes all look good! 🙌

cursor: { x: 0, y: 0 },
};

const slice = createSlice({
name: "state",
initialState,
reducers: {
setCursor: (state, action) => {
setCursor: (state, action: PayloadAction<Cursor>) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this type PayloadAction defined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

part of the redux-toolkit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should import it at the top of the code snippet because it’s an addition to the previous step.

Comment on lines +254 to +256
const others = useSelector<LiveBlocksState, LiveBlocksState["others"]>(
(state) => state.liveblocks.others
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct! This type annotation is unfortunately still necessary with the current version of @liveblocks/redux. The goal is to eventually not need this and instead let TypeScript be able to derive this type automatically, just like with our React and Zustand packages. But for now, this is indeed the way to type it 👍

export const {
suspense: {
RoomProvider,
useOthers, // 👈
},
} = createRoomContext(client);
} = createRoomContext< Storage, UserMeta>(client);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These type params need to be in a specific order. All of them are optional, but the order is always fixed. Each of these annotations are valid, but no others:

createRoomContext<Presence>(client);
createRoomContext<Presence, Storage>(client);
createRoomContext<Presence, Storage, UserMeta>(client);
createRoomContext<Presence, Storage, UserMeta, RoomEvent>(client);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exact, as we just start to introduce useOthers, we need to simply define a new Presence type in this code snippet and let the developer know that explain that a bit bellow.

@@ -337,7 +353,7 @@ function Index() {
scientist: new LiveObject({
firstName: "Marie",
lastName: "Curie",
}),
}) as Scientist,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we can avoid the as Scientist cast here and the type def in L341-344 — they should not be necessary. Did you run into issues with this? If so, please lmk, because it may be indicative of another issue?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with @nvie, no cast should be necessary here.

return Math.floor(Math.random() * max);
}

function getRandomColor() {
return COLORS[getRandomInt(COLORS.length)];
return COLORS[getRandomInt(COLORS.length)] as string;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type casts (using the as operator) are potential hiding places for bugs. I always try to avoid using as and I don't think we should advertise their use in our examples, because they're frowned upon.

Instead, did you perhaps mean:

function getRandomColor(): string {
  return COLORS[getRandomInt(COLORS.length)];
}

const [{ selectedShape }, setPresence] = useMyPresence();
const others = useOthers();

/* ... */

const onShapePointerDown = (e, shapeId) => {
const onShapePointerDown = (e: any, shapeId: string) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as with as operator, anys disable TypeScript and as a result they create places where bugs can lurk. For that reason, the use of any is frowned upon and we should not advertise that in our examples.

In this case — and in many other cases — you can replace this any by an unknown, which is safer:

Suggested change
const onShapePointerDown = (e: any, shapeId: string) => {
const onShapePointerDown = (e: unknown, shapeId: string) => {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah i hadn't seen that, I had seen most examples with events either imply an implicity "any" or use the above
will change this throughout

const { x, y, fill } = shape;

return (
<div
onPointerDown={(e) => onShapePointerDown(e, id)}
onPointerDown={(e:any) => onShapePointerDown(e, id)}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto for this any + all other instances of any in this file.

Copy link
Contributor

@GuillaumeSalles GuillaumeSalles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great start on a more tricky task that it seems!

I did a superficial review, I'll do another past tomorrow. There are a few things that are not necessarily obvious and conventions that we need to figure together. I'm going to help you on the small details :)

cursor: { x: 0, y: 0 },
};

const slice = createSlice({
name: "state",
initialState,
reducers: {
setCursor: (state, action) => {
setCursor: (state, action: PayloadAction<Cursor>) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should import it at the top of the code snippet because it’s an addition to the previous step.

import { createClient } from "@liveblocks/client";
import { createRoomContext } from "@liveblocks/react";
import { Presence, UserMeta, Storage } from "./types";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"./types" does not exist here and we didn't introduce Presence, UserMeta and Storage yet. These should come in a future step.


const client = createClient({
publicApiKey: "{{PUBLIC_KEY}}",
});


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra line to be removed

export const {
suspense: {
RoomProvider,
useOthers, // 👈
},
} = createRoomContext(client);
} = createRoomContext< Storage, UserMeta>(client);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exact, as we just start to introduce useOthers, we need to simply define a new Presence type in this code snippet and let the developer know that explain that a bit bellow.

@@ -176,15 +184,16 @@ we call this _presence_.
We can use _presence_ to hold any object that we wish to share with others. An
example would be the pixel coordinates of a user’s cursor:

```js
cursor: { x: 256, y: 367 }
```ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this step, I would not use any typescript definition.

@@ -12,8 +12,15 @@ import styles from "./Avatar.module.css";
*/

const IMAGE_SIZE = 48;
//convert to typescript
export interface AvatarProps {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be renamed to Props because we're in a single file component. This is a common convention.


export function Avatar({ picture, name }) {
//convert component to typescript
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To remove

@@ -2,6 +2,7 @@ import { AppProps } from "next/app";
import Head from "next/head";
import "../styles/globals.css";


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra line

@@ -11,7 +11,7 @@ import Cursor from "../components/Cursor";
import FlyingReaction from "../components/FlyingReaction";
import ReactionSelector from "../components/ReactionSelector";
import useInterval from "../hooks/useInterval";

////
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To remove

@@ -0,0 +1 @@
LIVEBLOCKS_PUBLIC_KEY="pk_dev_w56d3kiUtvotjLov9whFziBf"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To remove. It's probably missing from our .gitignore because keys should never be commited.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants