projectfluent / fluent.js

JavaScript implementation of Project Fluent
https://projectfluent.org/
Apache License 2.0
934 stars 77 forks source link

Generate types declarations #466

Open macabeus opened 4 years ago

macabeus commented 4 years ago

Currently we could define messages with variables:

hello = Hello { $name }
age = Your age is { $age }

and generating a bundle, we could use that on our code:

let hello = bundle.getMessage("hello")
console.log(bundle.formatPattern(hello.value, {name: "Anna"}))

let age = bundle.getMessage("age")
console.log(bundle.formatPattern(age.value, {age: "23"}))

But since there are lacking type informations, TS can't check if I'm writing all variables that message requires, or if I typed something wrong, as well as it's missing autocomplete.

bundle.formatPattern(hello.value); // I forgot to set the name
bundle.formatPattern(hello.value, {nam: 'Anna'}); // typo
bundle.formatPattern(hello.value, {age: 23}); // very bad!

We could improve even more if we could read the comments

# $name (String) - The user name
hello = Hello { $name }
bundle.formatPattern(hello.value, {name: 23}); // type error: should be string

On this small example, a valid type declaration would be:

type MessagesKey = 'welcome' | 'age'

type PatternArguments<T extends MessagesKey> = (
    T extends 'welcome'
        ? { name: string }
        :
    T extends 'age'
        ? { age: number }
        : never
)

export declare type Message<T> = {
    id: T;
    value: Pattern<T> | null;
    attributes: Record<string, Pattern<T>>;
};

export declare class FluentBundle {
  // inside of FluentBundle...

  getMessage<T extends MessagesKey>(id: T): Message<T>;
  formatPattern<T extends MessagesKey>(pattern: Pattern<T>, args?: PatternArguments<T>, errors?: Array<Error> | null): string;
}

image

stasm commented 4 years ago

This is a really interesting idea! I think it would improve the developer experience in TypeScript by a lot.

It would be interesting to see a proof-of-concept prototype of this. If you're up for it, I think the best place to start would be @fluent/syntax's Visitor class.

macabeus commented 4 years ago

@stasm Hey, I just launched the first proof of concept version: https://github.com/macabeus/fluent-typescript-loader 🎉 I would be happy if you could check that =]

At this moment, I'm generating the .d.ts file for just one .ftl. I'll have more work to could generate correctly the types on projects that has more than one .ftl file, because I'll need to merge that on just one file. Also we'll need to check if it'll work with fluent-react.

Anyway, fluent-typescript-loader already is working on the most simple case.


We would improve types such as { name: string | number } to be { name: string } when we add semantic comments on Fluent.

stasm commented 4 years ago

Very exciting, @macabeus! I can't wait to try this out in a project!

I looked at the implementation, and I'd like to provide some high level feedback:

I hope this help. Let me know if you have any questions!

macabeus commented 4 years ago

Very thank you for the review. I'll update the package following your suggestions, as well as change to use chokidar (or watchman?) instead of webpack loader, so we'll could use it without webpack.

macabeus commented 4 years ago

@stasm Hey, I just released a new version: https://github.com/macabeus/fluent-typescript/releases/tag/0.0.3 ! 🎉

Now I think that this tool is so much more stable. Could you take a review on codebase again, please?

Now I want to add support to fluent-react and react-i18next.

stasm commented 4 years ago

Nice work, @macabeus! I installed it in a test project, and I was impressed by the results!

It looks like the project is maturing nicely. I have two pieces of feedback, but overall it looks great. I hope more people will start using it!

Nice work!

macabeus commented 4 years ago

@stasm I was very busy on the last couple of weeks, but finally I added support to @fluent/react as well as react-i18next, and I added auto-emit when start to watch. Now it's a little more useful CLI tool to use with Fluent =]

https://github.com/macabeus/fluent-typescript/releases/tag/0.0.4