Skip to content

feat: add type declarations #193

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

Merged
merged 27 commits into from
Aug 17, 2020
Merged

Conversation

jamesonhill
Copy link
Contributor

@jamesonhill jamesonhill commented Jul 10, 2020

Initial pass at adding type declarations for each component's props as well as typings used by various event handlers. Feel free to comment or modify.

@kachkaev
Copy link

kachkaev commented Jul 10, 2020

For the types to be picked by TS, package.json will need this extra line:

   "main": "lib/index.js",
   "module": "es/index.js",
+  "types": "dist/index.d.ts",

Relevant PR: #86


If it helps, here are some typings we've got in one of our projects. It's a very modest starting point, but free to reuse any this code in your diff 🙂

declarations/react-base-table.d.ts

declare module "react-base-table" {
  export interface RowExpandEvent {
    expanded: boolean
    rowData: {
      id: string
      parentId?: string
    } & { [key: string]: any }
    rowIndex: number
    rowKey: string
  }
  export interface ExpandIconBaseProps {
    expandable: boolean
    expanded: boolean
    depth?: number
    onExpand: (expanded: boolean) => void
  }
  export const unflatten: any

  const Table: any

  export default Table

  export interface BaseColumnInfo {
    key: string
    width: number
  }

  export interface FrozenColumnInfo extends BaseColumnInfo {
    frozen: true
    label: string
  }
}

types/index.d.ts Outdated
/**
* The key field of each data item
*/
rowKey?: string | number;
Copy link
Contributor

Choose a reason for hiding this comment

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

type RowKey = string | number?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

isn't rowKey optional?

Choose a reason for hiding this comment

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

I think @nihgwu is suggesting to create a new separate type and then use it here as

rowKey?: RowKey

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 ok yea i can do that.

types/index.d.ts Outdated
/**
* Custom renderer on top of the table component
*/
overlayRenderer?: React.ReactNode | (() => React.ReactNode);
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe we could have a type CallOrReturn<T, P = {}> = T | ((p?: P, ...rest: any[]) => T)? I'm learning TS, not sure if thats the correct way

Copy link
Contributor

Choose a reason for hiding this comment

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

looks like this type is better type CallOrReturn<T, P = any[]> = T | (P extends any[] ? ((...p: P) => T) : ((p: P) => T))

Copy link
Contributor

@nihgwu nihgwu Jul 14, 2020

Choose a reason for hiding this comment

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

@jamesonhill WDYT or it's not intuitive?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It can definitely make some of the typings cleaner. It would be used for any props where the current typings are someType | () => someType ?

Copy link
Contributor

Choose a reason for hiding this comment

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

yes, and more, like CallOrReturn<React.ReactNode> and CallOrReturn<React.ReactNode, Args>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think i covered all of those types of instances in the changes i just pushed.

types/index.d.ts Outdated
* A callback function for the header cell click event
* The handler is of the shape of `({ column, key, order }) => *`
*/
onColumnSort?: ({ column, key, order }: SortByParams) => void;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should separate it from sortBy

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For compatibility reasons?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm thinking maybe something like

onColumnSort?: ({ column, key, order }): Pick<SortByParams, 'key' | 'order'> & { column: Column & { [key: string]: unknown } }

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean { key: string; order: SortOrder; column: ColumnProps }

@jamesonhill
Copy link
Contributor Author

I noticed in one of my repo's that there are multiple Column exports. Any suggestions on renaming the interface?

types/index.d.ts Outdated

type CallOrReturn<T, P = any[]> = T | (P extends any[] ? ((...p: P) => T) : ((p: P) => T));

export interface Column<T = unknown> {
Copy link

@kachkaev kachkaev Jul 15, 2020

Choose a reason for hiding this comment

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

How about ColumnConfig for this interface to avoid clashing with <Column />? @nihgwu WDYT?

It could be ColumnData but I'm personally not a big fan of data in interfaces because of how this term behaves when we need plurals:

type MyConfig = {/* */}

myConfig: MyConfig = {}
myConfigs: MyConfig[] = []

type MyConfigs = MyConfig[]
myConfigs: MyConfigs = []

vs

type MyData = {/* */}

myData: MyData = {}
myDatas: MyData[] = []    // 😬

type MyDatas = MyData[]   // 😬
myDatas: MyDatas = []     // 😬

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry I'm a newbie for TS, what about just IColumn or ColumnBase here? Sorry I still don't understand we have both Coumn and ColumnProps types here
Besides that, we allow arbitrary data in the column definition to solve the closure problem, maybe we should add [key: string]: any to the interface

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All of the component interfaces that have a column or columns prop (and there are quite a few) share the same type, which is Column here. The <Column /> component also has same props but some additional as well. So by having Column or IColumn that interface can be re-used across all column or columns instances. And by extending Column in ColumnProps there is a single source of truth for what is a "column". Alternative being to duplicating it many many times which isn't ideal.

Copy link
Contributor

Choose a reason for hiding this comment

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

column and Column should share exactly the same props, we will extract Column's props to column, Column is actually just a sugar syntax

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe ColumnBase or BaseColumnProps ?

types/index.d.ts Outdated

type CallOrReturn<T, P = any[]> = T | (P extends any[] ? ((...p: P) => T) : ((p: P) => T));

export interface Column<T = unknown> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry I'm a newbie for TS, what about just IColumn or ColumnBase here? Sorry I still don't understand we have both Coumn and ColumnProps types here
Besides that, we allow arbitrary data in the column definition to solve the closure problem, maybe we should add [key: string]: any to the interface

types/index.d.ts Outdated
* Custom column cell renderer
* The renderer receives props `{ cellData, columns, column, columnIndex, rowData, rowIndex, container, isScrolling }`
*/
cellRenderer?: CallOrReturn<React.ReactNode, {
Copy link
Contributor

Choose a reason for hiding this comment

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

for renderer we support element and elementType(function and class component), does this type support class component? like cellRenderer: ClassComponent

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm, i'm not sure but my inclination is that it does not. React.ReactNode is very generic but i think it only covers the returned element types. So it can be ReactElement | ReactFragment | ReactPortal | string | number etc... I'll double check though.

types/index.d.ts Outdated

type CallOrReturn<T, P = any[]> = T | (P extends any[] ? ((...p: P) => T) : ((p: P) => T));

export interface Column<T = unknown> {
Copy link
Contributor

@nihgwu nihgwu Jul 15, 2020

Choose a reason for hiding this comment

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

Can we just remove the T from Column? that would make the types clearer, I don't think the rowData should be constrained with a certain type, the only restriction is the unique id
Please correct me is I'm wrong, I'm still learning 😄

Choose a reason for hiding this comment

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

It's useful to have rowData typed, see Ant'd <Select> with a similar generic typing for value. Works like a charm for restricting wrong options and ensuring the onChange callback has the right typing too 👍

Each table instance would be able to set its own type and thus all all relevant props will be type-safe. For example, you won't be able to assign a callback that has a different expectation about the data.

types/index.d.ts Outdated
/**
* The sort state for the table, will be ignored if `sortState` is set
*/
sortBy?: SortByParams;
Copy link
Contributor

Choose a reason for hiding this comment

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

{ key: string; order: SortOrder }

types/index.d.ts Outdated
* A callback function for the header cell click event
* The handler is of the shape of `({ column, key, order }) => *`
*/
onColumnSort?: ({ column, key, order }: SortByParams) => void;
Copy link
Contributor

Choose a reason for hiding this comment

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

I mean { key: string; order: SortOrder; column: ColumnProps }

types/index.d.ts Outdated
}

export interface TableComponents {
TableCell?: CallOrReturn<React.ReactNode, {
Copy link
Contributor

Choose a reason for hiding this comment

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

they should be all elementType

types/index.d.ts Outdated
onExpand: (expanded: boolean) => void
}
export const unflatten: any;
export interface BaseColumnInfo {
Copy link
Contributor

Choose a reason for hiding this comment

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

what is this for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These are the types @kachkaev posted at the top of the thread that he said were used in your repo typings.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think those types should live in this repo

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed them

types/index.d.ts Outdated
width: number
}

export interface FrozenColumnInfo extends BaseColumnInfo {
Copy link
Contributor

Choose a reason for hiding this comment

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

what is this for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

from here: #193 (comment)

Choose a reason for hiding this comment

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

If the only distinction between frozen and normal column infos is frozen?: true | "left" | "right" could be worth having just one column info indeed 🤔

types/index.d.ts Outdated

export interface FrozenColumnInfo extends BaseColumnInfo {
frozen: true
label: string
Copy link
Contributor

Choose a reason for hiding this comment

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

what's this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

from here: #193 (comment)

Choose a reason for hiding this comment

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

Hmm this could be our app-specific code got blended into the library type definitions. Thanks for spotting @nihgwu !

types/index.d.ts Outdated
}

export interface FrozenColumnInfo extends BaseColumnInfo {
frozen: true
Copy link
Contributor

Choose a reason for hiding this comment

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

for frozen column it could be true | 'left' | 'right'

@nihgwu
Copy link
Contributor

nihgwu commented Aug 9, 2020

@jamesonhill @kachkaev Sorry for the late response, I'm pretty busy recently, I pushed my changes based on my local test demo, feedbacks are welcome

types/index.d.ts Outdated
*/
className?: string;
hidden?: bool;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

typo

@nihgwu
Copy link
Contributor

nihgwu commented Aug 12, 2020

any inputs here, we still need to add types for the utils, or we can address it in another PR

@jamesonhill
Copy link
Contributor Author

@nihgwu Everything that we have so far looks good to me. From what I can see, we're missing types for TableRow and TableHeader in addition to the utils. I think if we can at least get all of the components / props types in this PR that will be good. I can work on it some tonight. Any util types we can add here would be a bonus IMO.

@nihgwu
Copy link
Contributor

nihgwu commented Aug 12, 2020

@jamesonhill I don't know why I exported TableRow and TableHeader before which should not, so I think I'm OK without them, but the utils are useful, especially the unflatten for tree view

@jamesonhill
Copy link
Contributor Author

Gotcha, well then in that case i'll just focus on the utils.

@nihgwu
Copy link
Contributor

nihgwu commented Aug 14, 2020

@jamesonhill I’m planning to release this with #213 , so I did some work on the typings for utils last night

@nihgwu
Copy link
Contributor

nihgwu commented Aug 14, 2020

a lot of codes are copied from @sskiswani 's work in #93, great thanks

types/index.d.ts Outdated

export function normalizeColumns(elements: React.ReactNode[]): ColumnShape<any>[];

export function isObjectEqual(objA: any, objB: any, ignoreFunction?: boolean): boolean;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

why not type objA and objB as object?

@nihgwu
Copy link
Contributor

nihgwu commented Aug 15, 2020

let's move forward and hear feedbacks to improve the types

@nihgwu nihgwu changed the title WIP: Add type declarations feat: add type declarations Aug 16, 2020
@nihgwu
Copy link
Contributor

nihgwu commented Aug 16, 2020

@jamesonhill I got those error messages An index signature parameter type cannot be a union type. Consider using a mapped object type instead. for the use of [key: React.Key]: SortOrder; and expandedDepthMap: { [key: RowKey]: number }; and depthMap?: { [key: RowKey]: number }, in the console, any idea to fix them?

UPDATE: seems this commit could fix this issue 631d1bc

@nihgwu nihgwu force-pushed the ts-type-declarations branch from 01eae47 to 631d1bc Compare August 16, 2020 10:54
@jamesonhill
Copy link
Contributor Author

@nihgwu It's not wrong, TS just doesn't support union types for index signature types (at least last I checked). Using in is the correct way to work around it.

@@ -62,6 +63,7 @@
"@babel/plugin-transform-runtime": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"@types/react": "^16.9.46",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This lib uses react 16.4. Are the typings for 16.9 consistent w/ 16.4?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that should be OK as it's dev only to suppress the warning about the types from React

types/index.d.ts Outdated
minWidth?: number;
/**
* Whether the column is frozen and what's the frozen side
* Could be changed if we decide to allow Frozen.RIGHT
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This comment somewhat confuses me. Frozen right is already supported, or am I misunderstanding?

@nihgwu nihgwu merged commit 4231399 into Autodesk:master Aug 17, 2020
@nihgwu nihgwu deleted the ts-type-declarations branch August 17, 2020 02:25
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.

3 participants