typescript interface initialization - oop

My level of typescript is 'ABSOLUTE BEGINNER' but I have a good OOP background. I am building an with typescript that reference an external t.ds library that contains the following interface:
interface ISimpleObject {
foo: string;
bar?: any;
}
Now, if I want to call a method that has an IRequestConfig parameter, how do I create one? I can see different options:
Create a simple implementation of ISimpleObject. I don't like this approach because it looks like boilerplate code to me
don't initialize the object (I fear this could break something...):
var x :IsimpleObject;
x.bar = 'xxx';
callMethod(x);
Cast a pojo:
var x :IsimpleObject = <IsimpleObject>{foo: 'yyy', bar:'xxx'};
I don't like this approach either because it doesn't enforce type safety...
I guess this is a fairly trivial question and I am missing something trivial about typescript.

Typescript2:
const simpleObject = {} as ISimpleObject;

If you have an interface like:
interface ISimpleObject {
foo: string;
bar?: any;
}
This interface is only used at compile time and for code-hinting/intellisense. Interfaces are used to provide a rigorous and type-safe way of using an object with a defined signature in a consistent manner.
If you have a function using the interface defined above:
function start(config: ISimpleObject):void {
}
The TypeScript compile will fail if an object does not have the exact signature of the ISimpleObject interface.
There are multiple valid techniques for calling the function start:
// matches the interface as there is a foo property
start({foo: 'hello'});
// Type assertion -- intellisense will "know" that this is an ISimpleObject
// but it's not necessary as shown above to assert the type
var x = <ISimpleObject> { foo: 'hello' };
start(x);
// the type was inferred by declaration of variable type
var x : ISimpleObject = { foo: 'hello' };
start(x);
// the signature matches ... intellisense won't treat the variable x
// as anything but an object with a property of foo.
var x = { foo: 'hello' };
start(x);
// and a class option:
class Simple implements ISimpleObject {
constructor (public foo: string, public bar?: any) {
// automatically creates properties for foo and bar
}
}
start(new Simple("hello"));
Any time the signature doesn't match, the compile will fail:
// compile fail
var bad = { foobar: 'bad' };
start( bad );
// compile fail
var bad: ISimpleObject = { foobar: 'bad' };
// and so on.
There is no "right" way to do it. It's a matter of style choice. If it were an object that was constructed (rather than just directly passed as a parameter), I'd normally declare the type:
var config: ISimpleObject = { foo: 'hello' };
That way code-completion/IntelliSense will work anywhere I used the config variable:
config.bar = { extra: '2014' };
There is no "casting" in TypeScript. It is called a type assertion and shouldn't be needed in the cases described here (I included an example above where it could be used). There's no need to declare the variable Type and then use an assertion in this case (as the type was already known).

You can't create an instance of an interface since Typescript doesn't "translate" it into js. You can check the js that is created and you will see nothing in it. It's simple for compile errors, type safety and intelisense.
interface IStackOverFlow
{
prop1 : string;
prop2 : number;
}
public MyFunc(obj : IStackOverFlow)
{
// do stuff
}
var obj = {prop1: 'str', prop2: 3};
MyFunc(obj); // ok
var obj2 = {prop1: 'str'};
MyFunc(obj); // error, you are missing prop2
// getObj returns a "any" type but you can cast it to IStackOverFlow.
// This is just an example.
var obj = <IStackOverFlow> getObj();

Related

Declaring a function that is not an expression in Kotlin

How can I force a compile-time error, when somebody tries to use a function as expression that is not intended to be used like that?
fun someFunctionThatReturnsNothing() { println("Doing some stuff") }
// this should give an error:
val value = someFunctionThatReturnsNothing()
My use-case is generating a DSL where there can be name-clashes between DSL builders and sub DSLs in other builders depending on the scope of execution - e.g.:
// this is valid, calling RequestDSL.attribute(...) : Unit here,
val myRequest = request {
attribute {
name = "foo"
value = "bar"
}
}
// this is valid, calling AttributeDSLKt.attribute(...) : Attribute
val special = attribute {
name = "special"
value = "ops"
}
val myRequest = request {
extraAttribute = special
}
// this does not compile, but is confusing,
// because the compiler does not complain where the error was made
val myRequest = request {
// the user intends to call AttributeKt.attribute(...) : Attribute,
// but the compiler can only call RequestDSL.attribute(...) : Unit here
val special = attribute {
name = "special"
value = "ops"
}
// this is confusing and should already have been prevented above:
// >> Type mismatch. Required: Attribute. Found: Unit. <<
extraAttribute = special
}
If I could do something like RequestDSL.attribute(...) : void, the user wouldn't even be allowed to call attribute(...) as an expression inside the DSL. That would avoid the issue.
Can this be done somehow?
I tried Nothing, but it just makes all code unreachable after the function call.
I also tried Void, but it just force me to make the return nullable and return null instead of giving an error on the call side.

Kotlin DSL variable imitation

Using Kotlin type-safe builders one might end up writing this code
code {
dict["a"] = "foo"; // dict is a Map hidden inside that can associate some name to some value
println(dict["a"]); // usage of this value
}
This code is ok, but there is a problem: "a" is just a string. I want it to be like a user-defined variable - an identifier that is recognized by the compiler, auto-complete enabled.
Is there a way to turn it into something like this?
code {
a = "foo"; // now 'a' is not a Map key, but an identifier recognized by Kotlin as a variable name
println(a);
}
I can do this if I make code's lambda an extension function over some object with a field a defined inside. This is not what I want. I want to be able to use other variables (with unknown names) as well.
A possible workaround could be
code {
var a = v("a", "foo");
println(a);
}
Where v is a method of the extension's object, that stores value "foo" inside "dict" and also returns a handle to this value.
This case is almost perfect, but can it be clearer/better somehow?
You can use property delegation:
class Code {
private val dict = mutableMapOf<String, String>()
operator fun String.provideDelegate(
thisRef: Any?,
prop: KProperty<*>
): MutableMap<String, String> {
dict[prop.name] = this
return dict
}
}
Use case:
code {
var a by "foo" // dict = {a=foo}
println(a) // foo
a = "bar" // dict = {a=bar}
println(a) // bar
}

POJO class mismatch

I have the following class User that extends the BaseResponse class. I
am getting a type mismatch error:
Required => String
Found => String.Companion
for return apiKey
package com.touchsides.rxjavanetworking.network.model
import com.google.gson.annotations.SerializedName
class User: BaseResponse()
{
#SerializedName("api_key")
val apiKey = String
fun getApiKey(): String
{
return apiKey
}
}
abstract class BaseResponse(var error: String?=null)
{
}
How is the current implementation of this wrong
You used = instead : while declaration of api_key (apiKey = String). Which actually means you are initialising api_key with String.Companion Object.
And you don't need to create getApiKey() (getter) method as by default you will have getter method for your properties.
class User : BaseResponse() {
#SerializedName("api_key")
var apiKey: String? = null
private set
}
abstract class BaseResponse(var error: String? = null)
in fact you can use data class for this purposes
data class User(#SerializedName("api_key") val apiKey: String):BaseResponse()
fun main(args: Array<String>) {
Gson().fromJson<User>("{\"api_key\":\"my api key\"}", User::class.java).let {
println(it.apiKey)
}
}
A complete answer is that your code should look like this:
class User: BaseResponse()
{
#SerializedName("api_key")
lateinit var apiKey: String // must be set by something before being read
}
abstract class BaseResponse(var error: String?=null) {
}
You do not need a default value for the apiKey property if you intend to set it via deserialization later, if not then you should also add a default value as below. The getApiKey() method is removed because you do not need that in Kotlin, all properties have automatically generated getters built-in and by adding your own you would end up with a conflict between the generated getter and the one you manually created (two methods with the same name, same signature).
If you do need a default value for apiKey then stay with a var so that deserialization can work (if you intend to do that) and add a default empty string or make it a nullable string and set it to null.
class User: BaseResponse()
{
#SerializedName("api_key")
var apiKey: String = "" // if you want a default regardless, or make it nullable and null
}
abstract class BaseResponse(var error: String?=null) {}
You're stuck with the way Java do things. In kotlin when defining Getter and Setter, you don't have to write them yourself. Once you declare a variable, both methods would be automatically created.
EDIT: So delete the getter in your POJO class.

How to alias complex type constructor in typescript?

I am using a custom library, and for some types I have to write:
import * as pipeline from 'custom-pipeline';
import {MapTransform} from 'custom-pipeline';
export const createTransform = (appConfig: IAppConfig):
MapTransform<ISomeData, IFoo> |
MapTransform<ISomeData, IBar> => {
switch(appConfig.createType) {
case 'foo':
return new pipeline.MapTransform<ISomeData, IFoo>((data: ISomeData) => {...});
case 'bar':
return new pipeline.MapTransform<ISomeData, IBar>((data: ISomeData) => {...});
}
}
Especially the verbose constructor is irking me. I can I alias the types alright:
type FooTransform = MapTransform<ISomeData, IFoo>;
type BarTransform = MapTransform<ISomeData, IBar>;
Yet I cannot do:
new FooTransform((data: ISomeData) => {...});
new BarTransform((data: ISomeData) => {...});
throwing an error like:
error TS2304: Cannot find name 'FooTransform'.
I assume it's because I only have a type and not a class? Yet how can I alias the constructor in a way that I can do new FooTransform as above?
The definition of MapTransform looks like:
export declare class MapTransform<A, B> extends BaseTransform<A, B> {
constructor(private mapFunc: (val: A) => B);
}
I can reduce the constructor to this:
fooMapFunction = (data: ISomeData): IFoo => {...};
new MapTransform<ISomeData, IFoo>(mapFunction);
Albeit it's not en par with a new FooTransform(fooMapFunction).
Your assumption is correct, type declarations are compile-time only, thus you can't perform any operations on them (including instantiation with new).
Solution 1: Type Assertion
Let's assume the superclass looks like this:
class MapTransform<T1> {
constructor(public readonly data: T1) { /* ... */ }
}
To create a specialised type alias, you can do this:
type TransformA = MapTransform<number>;
From the perspective of the superclass MapTransform, there's no difference between MapTransform<A>, MapTransform<B> and so on (that's the point of generics), so we can safely assign the constructor function of class MapTransform to a constant TransformA. Calling new TransformA() is, at runtime, identical to calling new MapTransform<number>():
const TransformA = <{ new (data: number): TransformA; }>MapTransform;
Notice the type assertion? This tells the TypeScript compiler to treat the assigned value MapTransform as an object which constructs an object of type TransformA when instantiating it with new. We can now write:
const a = new TransformA(123);
a.data.toExponential(); // works
BTW, what the TypeScript compiler actually sees is this ...
const TransformA = <{ new (data: number): MapTransform<number>; }>MapTransform;
... because type TransformA ≡ MapTransform<number>.
Be aware that all of these will evaluate true:
new TransformA(123) instanceof TransformA
new TransformA(123) instanceof MapTransform
new MapTransform<number>(123) instanceof TransformA
new MapTransform<number>(123) instanceof MapTransform
Here's the example in the TypeScript Playground.
Solution 2: Subclassing
Again, let's assume the superclass looks like this:
class MapTransform<T1> {
constructor(public readonly data: T1) { /* ... */ }
}
With subclassing, the solution is simple: Extend the superclass and pass the desired type parameters along in the extends clause.
class TransformA extends MapTransform<number> { }
Et voilà, you now have a constructor that works at runtime as well as a type that works at compile-time.
Unlike the first solution, the following 2 expressions will evaluate true ...
new TransformA(123) instanceof TransformA
new TransformA(123) instanceof MapTransform
... while these will evaluate false:
new MapTransform<number>(123) instanceof TransformA
new MapTransform<number>(123) instanceof MapTransform
Solution 2.1: Anonymous Subclassing
If you only need the constructor alias but not the type alias, this might come in handy:
const TransformA = class extends MapTransform<number> { };
This is called a class expression and can be used like every other expression, for example:
class App {
private static TransformA = class extends MapTransform<number> { };
public doStuff() {
return new App.TransformA(123);
}
}
More on Types
If you're interested, here's a few more links on the topic:
Named Types in TypeScript Spec
Type Assertions
TypeScript Handbook: Interfaces, specifically section "Difference between the static and instance sides of classes"
Class Expressions
EDIT
You wrote you've had issues applying these solutions to a class that expects a function as a parameter, so here's another example in the TypeScript playground.

TypeScript Interface signature for property without setter.

We have a typical getter in one of our classes, lets say
class Employee implements IEmployee {
private _fullName: string;
get fullName(): string {
return this._fullName;
}
}
and an interface to work with it
interface IEmployee{
fullName: string;
}
When working with an instance via this interface the compiler will not warn us about absence of a setter if we try to assign to fullName, and the the JS runtime simply swallows any assignment and does not throw an error. Is there any way to mark interface member as having only getter or only setter?
I've seen this post, but it is quite old, i want to know, if anything improved.
Properties in typescript can now have 'readonly' modifier, which achieves the desired restult.
interface IEmployee{
readonly fullName: string;
}
This is an interesting question. The concept of a readonly property is subtly different in TypeScript to other languages.
In many languages a property with a getter (but no setter) would raise a compiler error if you attempted to set the property, but TypeScript doesn't.
The property is still readonly, because it makes no difference if you attempt to set it; the set will fail silently.
Here is an example without any interfaces:
class Example {
get name() {
return 'Steve';
}
}
var y = new Example();
y.name = 'Example 2';
alert(y.name);
There is no compiler warning when I use x.name = 'Example 2';.
If there was a compiler warning, I would subsequently expect there to be a way of specifying the readonly-ness of a property within an interface. As you'd expect though, given the above information, you can't set a readonly property on an interface.
interface Test {
name: string;
}
class Example {
get name() {
return 'Steve';
}
}
var x: Test = new Example();
x.name = 'Example 1';
alert(x.name);
var y = new Example();
x.name = 'Example 2';
alert(x.name);
This means you can only enforce readonly-ness by having a method to get the value of the property (and obviously no method that allows it to be set).
interface Test {
getName: () => string;
}
class Example {
getName() {
return 'Steve';
}
}
var x: Test = new Example();
//x.getName('Obviously not');
//x.getName() = 'Obviously not';
alert(x.getName());
var y = new Example();
//y.getName('Obviously not');
//y.getName() = 'Obviously not';
alert(y.getName());