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

Custom and Extended Types #3785

Closed
wants to merge 66 commits into from
Closed

Conversation

mdmjg
Copy link
Contributor

@mdmjg mdmjg commented Jul 13, 2020

Is this adding or improving a feature or fixing a bug?

feature

What's the new behavior?

Uses interfaces to support declaration merging and types for type discrimination.

In order to use declaration merging, the user must augment the Slate module. This detail could be explained in an additional example that could be added for this feature.

Have you checked that...?

  • [ x] The new code matches the existing patterns and styles.
  • [ x] The tests pass with yarn test.
  • [ x] The linter passes with yarn lint. (Fix errors with yarn fix.)
  • [ x] The relevant examples still work. (Run examples with yarn start.)

Does this fix any issues or need any specific reviewers?

Fixes: #3725 #3744
Reviewers: @thesunny @timbuckley @BrentFarese

Copy link
Collaborator

@BrentFarese BrentFarese left a comment

Choose a reason for hiding this comment

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

See requested changes/comment inline. Thanks.

* The `CustomExtensions` interface extends types
*/

export interface CustomExtensions {
Copy link
Collaborator

@BrentFarese BrentFarese Jul 16, 2020

Choose a reason for hiding this comment

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

I still think we need to update Slate's types for Text and Element to use ExtendedType below? See @thesunny suggestion in the implementation issue - #3725. For instance:

// This would be Element as per Slate's definition
export type BaseElement = {
  children: Text[]
}

// This would be Text as per Slate's definition
export type BaseText = {
  text: string
}

export type Text = ExtendedType<"Text", BaseText>
export type Element = ExtendedType<"Element", BaseElement>

Then, the exported new types for Text and Element would be used in Slate's codebase and any developer's use of CustomExtensions and ExtendedType would propagate to the rest of the codebase.

Let's discuss if needed.

Copy link
Contributor Author

@mdmjg mdmjg Jul 16, 2020

Choose a reason for hiding this comment

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

The issue with this is that when you use ExtendedType you are redefining Element or Text according to the definition you gave it inside your CustomExtension (once the user has defined it). If make the extended Element the new Element, a bunch of errors will be thrown during build, and pretty much 60% of the testcases will fail. Something that I found while making this PR was that in order to call Element's functions like isElement, you need to use an alias. If you take a look at my isElement test case, you will see I am calling BaseElement.isElement. If I were to use Element.isElement, the program would not recognize the function because the type Extension is not able to fetch the functions as well.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sorry, I haven't had time to dive into the code so perhaps you could explain the issue and I might be able to help with a solution.

My question is, if the original object is defined as BaseText or BaseElement, and the extended type is created as Text and Element, are you saying that the methods don't come through to the extend type? It looks like in the typing we are doing:

export type ExtendedType<K extends string, B> =
  unknown extends CustomExtensions[K]
  ? B
  : B & CustomExtensions[K]

So shouldn't we get the proper types from both the original with the extensions added (ie. B & CustomExtensions[K])?

I may be misunderstanding the issue in which case a clarification would be helpful. Thanks.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@thesunny could use a review from your end. I think the code is looking more final now so it's probably a good time to review. Thanks!

@CameronAckermanSEL can you review too?

@@ -468,7 +472,7 @@ export const Node = {
if (Text.isText(node)) {
return node.text
} else {
let children = node.children as Array<Descendant>
const children = node.children as Array<Descendant>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Array<x> is more commonly written as x[]

Suggested change
const children = node.children as Array<Descendant>
const children = node.children as Descendant[]

Copy link
Collaborator

Choose a reason for hiding this comment

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

Tim is totally correct but we should avoid the type assertion, if we can.

@@ -217,7 +217,7 @@ export const createEditor = (): Editor => {
(editor.isInline(node) ||
node.children.length === 0 ||
Text.isText(node.children[0]) ||
editor.isInline(node.children[0]))
editor.isInline(node.children[0] as Element))
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should be staying away from type assertions, if at all possible. There is a way to have the same code here w/o the type assertion.

@mdmjg
Copy link
Contributor Author

mdmjg commented Jul 21, 2020

Note: You will get some errors when you run yarn build because of the placeholder property in the Leaf interface in slate-react. Some help with that would be super appreciated!

@@ -28,6 +27,8 @@ const Leaf = (props: {
<String isLast={isLast} leaf={leaf} parent={parent} text={text} />
)

// const placeholder = 'placeholder' in leaf && leaf.placeholder
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need this @mdmjg? Let's remove.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ill remove

@@ -14,7 +14,7 @@ import { EDITOR_TO_ON_CHANGE } from '../utils/weak-maps'

export const Slate = (props: {
editor: ReactEditor
value: Node[]
value: Element[]
children: React.ReactNode
onChange: (value: Node[]) => void
[key: string]: unknown
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we might be able to get rid of this [key: string]: unknown.

@@ -0,0 +1,18 @@
import { Descendant } from './node'

export interface BaseElement {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think I would move BaseElement next to the Element type and BaseText next to the Text type (i.e., back in the interface files for those types). Easier to read/follow the source that way.

@@ -65,7 +65,8 @@ export const GeneralTransforms = {
if (Text.isText(node) && Text.isText(prev)) {
prev.text += node.text
} else if (!Text.isText(node) && !Text.isText(prev)) {
prev.children.push(...node.children)
const children = node.children as Element[]
Copy link
Collaborator

Choose a reason for hiding this comment

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

I changed the type of editor.children to Descendant[] instead of Element[] and am going to revert some of these changes. We can discuss but I think the more appropriate type for editor.children is Descendant[].

Copy link
Contributor Author

Choose a reason for hiding this comment

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

okay! I'll wait for you push then

@timbuckley timbuckley force-pushed the custom-types branch 2 times, most recently from 889781c to 7a95033 Compare August 11, 2020 20:41
@lcswillems
Copy link
Contributor

lcswillems commented Aug 17, 2020

This seems an amazing PR!!

I don't get why @CameronAckermanSEL, @ianstormtaylor and @thesunny should review this PR? They don't seem active. Waiting for their review will just make this PR never get merged and I don't think it is the best thing for the community.

I would suggest that this PR got merged and if there are issues, they will be raised and addressed in a future release, it is not like if the project was currently in a stable state.

Thank you @mdmjg for the awesome work you did in this PR and previous ones (#3762, #3784)!

@@ -9,6 +9,7 @@ import {
Transforms,
Path,
} from 'slate'
Copy link
Collaborator

Choose a reason for hiding this comment

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

is this a potential circular dependency?

packages/slate/src/interfaces/node.ts Show resolved Hide resolved
packages/slate/src/transforms/node.ts Show resolved Hide resolved
support/fixtures.js Show resolved Hide resolved
@cameracker
Copy link
Collaborator

Thank you so much for all the effort put into this PR. This is going to be a great improvement for users who are in typescript.

I had a few small questions about some code that looks like it was changed for reasons perhaps unrelated to this PR. Additionally, there was some code in the fixtures function that was changed that we might want to change back.

The final concern I had was that there was a pretty massive amount of churn in yarn.lock but no corresponding changes to package.json. I think we may want to revert yarn.lock to what it was, unless these changes can be justified. Surprise changes to the lock file are a little bit of a red flag to me.

Thanks again!

@BrentFarese
Copy link
Collaborator

Closing this in favor of #3835.

@BrentFarese BrentFarese deleted the custom-types branch August 21, 2020 20:11
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.

Add Custom TypeScript Types to Slate Support TypeScript declaration merging
6 participants