Compiling enterprise

Ivan Koshelev blog on software development

Pragmatic uses of TypeScript type system

My type of type

You may have heard, that TypeScripts type system has been prooven turing-complete . In layman’s terms this means you can write type definitions in such a way, that compiler will execute a simple program from just trying to compile them. A fascinating fact, but how is it useful in our daily work? It tells us, that TS type system is on a whole next level compared to what we are used to in Java and C#, and we should rethink our approach to it. In this article, we won't delve into the arcana of things like conditional types, but will concentrate on the often overlooked (especially by devs coming from the static typing world) practical uses of TS type system, that make daily life of a dev much easier.

Robert C. Martin of Clean Code fame once said, it would be nice if we could instruct compiler to use concrete types in Java only for their signature of abstractions, i.e., interfaces. Indeed, for 95% of enterprise code I work with, formal interface types serve as a technical detail, needed by the compiler only. Such case is easy to recognize: your interface has only 1 implementation (not counting testing mocks) and you don't have to ship/deploy this implementation separately (it is in the same DLL as interface, or the dll is separate, but always goes together). This is a clear sign:

  • this interface is driven by a single use case, which is fully reflected in single concrete implementation
  • only needed to teach compiler about proper dependency abstraction
  • you as a dev don't get anything out of it, worse yet, you are forced to duplicate member definitions
Ask yourself, how many interfaces in your application can be resolved by your IoC container automatically? Realizing this, I always wanted to be able to decorate my C# classes with a keyword or attribute, that would give me an automatic interface based on their concrete implementation and containing all their public members. Well, guess what we can do in TS?

class Foo {
    bar: number;
    baz() { console.log(this.bar); }
}

// An interface extends a class!
interface IFoo extends Foo { }

let foo = {} as IFoo;

let bar:number = foo.bar;
foo.baz();                  //type checks ok!

TS playground

And that is just the tip of the iceberg, real power comes in type!

continue reading

Roslyn beyond 'Hello world' 04

Tips and shortcuts for success

Working on Roslyn extensions is a bit different from day-to-day enterprise development. You have to get used to working with immutable trees of data representing everything in a solution. You also have to keep in your head, which parts of code map to nodes, which to tokens and other code tree representation subtleties. This article will teach you a few things to get results faster and alleviate the tedium.

continue reading

Advanced usage of interpolated strings in C#

One of the best new features of C# 6 was string interpolation. It is much nicer to read, and you no longer had to rely on 3rd party libraries remembering to introduce ‘DoSomethingFormat(string, params object[])’ along with just ‘DoSomething(string)’.

But ‘DoSomethingFormat’ has one big advantage over string interpolation when it came to unit tests – you can assert on particular values direcly, without having to parse them out of an applied string template. If your date or number format suddenly changes, you don’t need to change your Unit Tests (provided, of course, the format itself is not important enough, just the data).

Well, good news – interpolated strings have a rarely mentioned way for you to have the best of both worlds - FormattableString type!

continue reading

Roslyn beyond 'Hello world' 03

Symbol Graph and analyzer diagnostics

In the previous article we looked at the result of parsing stage of compilation - syntax graph, how it represent information available to C# compiler from analyzing a single file of code and, consequentially, reflects that text quite faithfully and how to transform it to refactor existing code. In this article we will look at symbol graph - result of the next stage of compilation - binding.

In the syntax graph you can see that core C# concepts are represented by corresponding node type, like ClassDeclaration or MethodDeclaration. Inside they often contain IdentifierTokens representing the textual name of some type, but that is as much information as you initially get. In order to get information about a particular type, like its namespace or members, you’d need to get its symbol by performing a costly binding process – check which namespaces are currently used in the file, look for type declarations through the entire solution and all imported DLLs that match, compile them if need be, etc. - compile everything.

The symbol graph allows you to answer a lot more meaningful questions than syntax graph. Want to track down all usages of a specific type? Rename something solution-wide? You can do that.

You can trigger a limited binding for a given piece of syntax graph, or you can hook into the compilation process of the IDE itself (it is continuously recompiling your solution on every change to warn you of any errors as you type away). This common way to do this is by creating a class inheriting from DiagnosticAnalyzer.

continue reading

Roslyn beyond 'Hello world' 02

Visual Studio extension for refactoring

From the point of view of developer using it (we will call them 'user-dev'), Refactorings in Roslyn are additional commands that pop-up in Visual Studio when they click certain pieces of code. From our point of view, Refactorings are classes inheriting from CodeRefactoringProvider, which get a chance to examine current syntax graph every time user-dev clicks something in it and determine, if they should offer any transformations of that graph based on its state and what was clicked.

We will be building a Refactoring which allows our user-dev to regenerate a given classes public constructor by adding to it any missing assignment of members that match a certain pattern and are not yet assigned during construction. Specifically, this is the refactoring we use at work to regenerate dependency injected constructors.

continue reading