Skip to content
This repository has been archived by the owner on Jun 18, 2019. It is now read-only.

Split types.ts #68

Closed
pbjorklund opened this issue Apr 26, 2016 · 15 comments
Closed

Split types.ts #68

pbjorklund opened this issue Apr 26, 2016 · 15 comments

Comments

@pbjorklund
Copy link
Contributor

pbjorklund commented Apr 26, 2016

Should we split the interfaces in types.ts to own files?

Usage of type is now import * as Types from "./types"; will have to become import { Type } from "./types/type.ts";

@patrick-rodgers
Copy link
Contributor

I keep going back and forth on this one in my head. These are just type defs / interfaces to help with typing return values and parameters - likely would never need to use them separately. We could move to individual files and play with the TypeScript namespace/module merging and see. I would like to keep the ability to just include "types" whatever the syntax is - and not have to include lots of files if I needed to use more than one type. Splitting it does make sense as these grow, will be much easier to find and update stuff - but there is always ctrl+F. Someone can convince me either way probably, let's discuss on this weeks call and make a decision.

@pbjorklund
Copy link
Contributor Author

Assign this to me and I'l create a PR that shows it in action and we can decide if good or bad idea after.

@patrick-rodgers
Copy link
Contributor

All set, thanks for having a look.

@pbjorklund pbjorklund mentioned this issue Apr 30, 2016
@pbjorklund
Copy link
Contributor Author

pbjorklund commented May 5, 2016

Points to be dealt with:
[ ] Find the best way to reference all types at compilation/code time without having to import {} every type by itself
[ ] The locale enum adds 7kb to the package. Should it be included? Bigger question: Should we allow cherry picking from the library, see #87
[ ] Exclude "empty type files" from the packaging process since it just adds LoC for no reason. Investigate http://www.typescriptlang.org/docs/handbook/declaration-merging.html

(New PR opened in #89, action points extracted from #77)

@pbjorklund pbjorklund mentioned this issue May 5, 2016
@patrick-rodgers
Copy link
Contributor

Looked at the PR - I see all the types broken out, but not sure which of the above points it covers? I guess my 2c:

  • one file does that
  • I don't think we need a 7k enum, but that's me. Also, I don't think we are blocking cherry picking/tree shaking/whatever, at least not intentionally. No one has pointed out what changes (if any) we need to make to support this. Not a high priority for me currently, but not against it either.
  • Yes, excluding empty code blocks just makes sense.

@pbjorklund
Copy link
Contributor Author

The [ ] were meant to indicate a "todo" for myself :)

@pbjorklund
Copy link
Contributor Author

pbjorklund commented May 16, 2016

https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines states

Shared types should be defined in 'types.ts'.

And in https://github.com/Microsoft/TypeScript/blob/master/src/compiler/types.ts we can see that they mix types/interfaces/enums in their types.

A file/module with only an interface export generates an interfaceName.js file with "use strict"; as the only content. That's minus one for a pure file for each interface.

So I started to look at how vscode does it and I kinda like their approach.

Let's take https://github.com/Microsoft/vscode/blob/5b9ec15ce526decc5dd0488339e798f6bcb4ec46/src/vs/base/common/event.ts

They export interfaces alongside with the implementation of the thing that is used which looks clean to me.

This leads to files like https://github.com/Microsoft/vscode/blob/5b9ec15ce526decc5dd0488339e798f6bcb4ec46/src/vs/editor/common/commonCodeEditor.ts with many import statements.

I myself like the readability and clear intent of specifying what we import into a class vs using import *.

Side note: I have no idea what's going on at the end of their https://github.com/Microsoft/vscode/blob/5b9ec15ce526decc5dd0488339e798f6bcb4ec46/src/vs/base/common/types.ts

So heres my two cents that we can discuss -

  1. If you write a class, write an interface for the class in the same file. Generalize the interface and concretize the class. Iterator = interface, ListIterator = class
    1.1 If the interface is shared between multiple files, export the interface in /src/types/types.ts
  2. If you write a function, consider writing an interface for the function for readability and clarity inside the module. If it is shared put it inside /src/types/types.ts
  3. If you write an enum put it inside /src/types/enums as a separate file

Edit: Let me know if this is unclear or if anyone wants me to cook up some sample code to show how these style guidelines could be implemented on any current piece of code. Not doing that before we either accept this proposal or someone asks me to ;)

@pbjorklund
Copy link
Contributor Author

Re cherry picking perhaps this is what @mike-morrison is talking about in #85. I think we should solve that issue before deciding on the Locale enum as they are interconnected.

@mike-morr
Copy link
Contributor

@pbjorklund Wow! That vscode is beautiful. It's so consistent in style. Speaking of, can we relax the tslint rules a bit. Seeing the squgglies and having to delete white space to make them go away is getting on my nerves for something that is going to get transpiled and minified anyways. CRLF at the end of files, etc. Is there some other gain I am missing?

@pbjorklund
Copy link
Contributor Author

@patrick-rodgers any thoughts on the above before I start?

@patrick-rodgers
Copy link
Contributor

I don't see any value in duplicating all the classes as interfaces, and I don't see that supported by the files you linked. They are exporting both interfaces and classes as are we - but not 1 interface per class. Also, I've been a fan of keeping all the common utility types in a single types file from the beginning. They are also using both import syntaxes as appropriate; * and {} as are we.

Classes in TS are essentially also interfaces. If I have a class and a function like:

class foo {
public a: string;
public b: string
}

function something(f: foo) {...}

I can already do the below because the type checking is structural.

something({ a: "Hello", b: "World" });

Implementing a rule that for every class we write we also have and maintain a duplicate interface doesn't add any value or make consumption easier, it just increases overhead. Not sure this is worth pursuing.

@pbjorklund
Copy link
Contributor Author

I don't see any value in duplicating all the classes as interfaces, and I don't see that supported by the files you linked.

I like to program against interfaces instead of implementations, perhaps my background from other languages are taking over to much compared to what the TS best practices/environment are.

When you use a class as an interface in TS you also get the internals of the class.
Here's a short example of what I mean in a snippet

Also, I've been a fan of keeping all the common utility types in a single types file from the beginning.

I want us to agree on something and put it in a styleguide for when other people come along and start thinking about the same thing I did.

Is it the implied "always" you don't like? Then how about something like:

  1. If you write a class, consider writing a generalized interface for the class in the same file.
    E.g Iterator = interface, ListIterator = class.

1.1 If the interface is shared between multiple files, export the interface in /src/types/types.ts

When it comes to functions that have complicated signatures I like to have an interface for them, just makes life easier, but don't have special other reasons besides that.

If no-one cares about discussing things like these I shouldn't waste my energy and go hack on some other part of the library. Just let me know.

@patrick-rodgers
Copy link
Contributor

I think we are using interfaces where it makes sense, for provisioning providers for example. Maybe there are places where we could make things more generic, on the whole I think we are in pretty good shape. Take web for example - will we realistically have separate web implementations such that we need an IWeb (I know you are opposed to using the "I" prefix)? While I agree with your point 1, it is just good programming practice to use interfaces when appropriate - so do we need to call that out for folks?

As for the location, we could put them all in types.ts - currently we are keeping the generic ones we don't own (i.e. those that come from the service declarations) in types and the ones we own near where they are used (again I'll use the example of configuration providers). That seems easier to me to edit, find things since we will likely change our interfaces, but never (until there is a service update) the ones we don't own, so stuffing them in a giant long file is less of a pain.

I am not sure what you mean regarding interfaces for functions - so you mean the parameters? So instead of function f(x,y,z,f,e,s,a,r,fg,d,ad) it would be f(foo: IFoo)?

@pbjorklund
Copy link
Contributor Author

Take web for example - will we realistically have separate web implementations such that we need an IWeb

It's very useful to be able to easily switch out the implementation when doing unit tests or functional tests where you want to isolate what effect a dependency has on a function (testing glue for instance).

I know you are opposed to using the "I" prefix

After doing research it seems that there is no consensus at all in the TS community... Let's just pick a standard and go with it, that's more important then discussing forever how to name them and never actually getting to implement the names. And when I saw that the SharePoint Framework uses INaming I think we should probably go with that as well. If you are ok with that I'l go ahead and add it to the style guide.

so do we need to call that out for folks?

Not sure if that's a question or a statement?

As for the location, we could put them all in types.ts - currently we are keeping the generic ones we don't own (i.e. those that come from the service declarations) in types and the ones we own near where they are used (again I'll use the example of configuration providers).

How about this for a section in the style guide?:

Interfaces

When writing a generic interface that is used at multiple locations or the interface is external it should be put into /src/types/types.ts
Interfaces we own should be included alongside the concrete implementation in the same file

That seems very close to the latest proposition I had, or am I missing something?

I am not sure what you mean regarding interfaces for functions - so you mean the parameters?

One usage for that is typing configuration objects, but I don't have a real preference on lots of params vs config objects as long as they have no behavior. Also not sure if typing functions actually give anyone but me any extra value, I just like to be able to read "function contracts" separately, but due to the ... promiskuity? of JS I don't know a way to make them useful in an editor or at compiletime. Let's not include that one at all for now.

So if we can agree on that I can go hunting across the codebase and see if we already follow this or not. If we already do it this way it's great because then we would already be in line with what we write in the style guide.

@patrick-rodgers
Copy link
Contributor

Going to close this one for now, please reopen it if you have an update or any progress to add. Been open for a bit with no activity.

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

No branches or pull requests

3 participants