Child class properties cannot be set from Parent class's constructor - javascript

I´m using this exact code in nextjs and it always log
class A { // A: Model
constructor(source){
Object.keys(source)
.forEach(key => {
if(!this[key]){
this[key] = source[key];
}
});
}
}
class B extends A{ // B: User
first_name: string;
}
let v = new B({first_name: "nof"});
console.log(v)
// B { first_name: undefined }
But when using in non nextjs project it just work and shows both n1, and m2
I belive this is related to some configurations on nextjs; any idea on how to achieve that without declarign a constructor in B class ?
This is the original example, kept here as a references for the answers below:
class A {
constructor(){
this.init()
}
init(){
this["n1"] = "nof";
this["n2"] = "nof";
}
}
class B extends A{
n1: string;
}
let v = new B();
console.log(v)
// B { n1: undefined, n2: 'foo' }

The result is as expected. When constructing a new object B it first constructs A so then both n1 and n2 are set to 'nof'. Then B is constructed and overwrites n1 with an unset(always undefined) variable so n1 = undefined and n2 should still be 'nof'.

You're seeing that behavior because apparently you now have useDefineForClassFields enabled, where before you didn't have it enabled. From that link:
Use Define For Class Fields - useDefineForClassFields
This flag is used as part of migrating to the upcoming standard version of class fields. TypeScript introduced class fields many years before it was ratified in TC39. The latest version of the upcoming specification has a different runtime behavior to TypeScript’s implementation but the same syntax.
This flag switches to the upcoming ECMA runtime behavior.
You can read more about the transition in the 3.7 release notes.
What's happening is that after A's constructor has finished running, B's (implicit) constructor redefines the property (replacing the one A defined), losing the value A created in the process. As noted in the quote above, JavaScript does the same thing:
class A { // A: Model
constructor(source){
Object.keys(source)
.forEach(key => {
if(!this[key]){
this[key] = source[key];
}
});
}
}
class B extends A{ // B: User
first_name/*: string*/;
}
let v = new B({first_name: "nof"});
console.log(v)
// B { first_name: undefined }
You could turn that setting off to get the output you want from that code; playground link (I haven't tried to fix the other type errors in that). But I strongly recommend you update the code instead. Just from a design perspective, A shouldn't be initializing properties that it doesn't define. Moreover, it's not typesafe to dump all of the properties of an object you receive into the instance you're initializing.
I would suggest simply writing the constructors and having them copy the data they're supposed to copy. Amongst other things, that prevents the instances from getting properties from source that don't exist on them as in this JavaScript example (TypeScript version here [again, I haven't tried to fix other type errors there]):
class A { // A: Model
constructor(source){
Object.keys(source)
.forEach(key => {
if(!this[key]){
this[key] = source[key];
}
});
}
}
class B extends A{ // B: User
first_name;
}
let v = new B({first_name: "nof", shouldNotBeHere: "Hi there!"});
console.log(v);
// B { first_name: "nof", shouldNotBeHere: "Hi there!" }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
But again, if you want to have A do that blind copy, you can make the compiler error (but not the problem) go away by turning off the flag.

Can you try to explicitly declare the type of "n1" property in class B by adding a type declaration:
n1!: string;
The "!" symbol after the property name is used to declare a non-null property, which ensures that "n1" will always have a value.

Related

How can I make dynamically generated getter and setters known to TypeScript's compiler?

The following code works without any errors in JavaScript:
class Test {
constructor() {
Object.defineProperty(this, "hello", {
get() {return "world!"}
})
}
}
let greet = new Test()
console.log(greet.hello)
But TypeScript throws this error:
Property 'hello' does not exist on type 'Test'.
Update
This is similar to what I need and I was trying. Playground link.
I want compile time check for properties becasue they might change in future.
class ColorPalette {
#colors = [
["foregroundColor", "#cccccc"],
["backgroundColor", "#333333"],
["borderColor", "#aaaaaa"]
];
constructor() {
this.#colors.forEach((e, i) => {
Object.defineProperty(this, this.#colors[i][0], {
enumerable: true,
get() { return this.#colors[i][1]; },
set(hex: string) {
if (/^#[0-9a-f]{6}$/i.test(hex)) {
this.#colors[i][1] = hex;
}
}
})
});
}
toString(): string {
return Object.values(this).map(c => c.toString().toUpperCase()).join(", ");
}
}
let redPalette = new ColorPalette();
// redPalette.foregroundColor = "#ff0000"; <----- error: "Property 'foregroundColor' does not exist on type 'ColorPalette'"
console.log(redPalette.toString());
There are two things standing in the way of your code working as-is.
The first is that you cannot declare class fields implicitly inside the constructor method body in TypeScript code. If you want a class to have a property, you will need to explicitly declare this property outside the constructor:
class ColorPalette {
foregroundColor: string; // <-- must be here
backgroundColor: string; // <-- must be here
borderColor: string; // <-- must be here
// ...
There's a declined suggestion at microsoft/TypeScript#766 asking for such in-constructor declarations, and an open-but-inactive suggestion at microsoft/TypeScript#12613 asking for the same, but for the foreseeable future, it's not part of the language.
The second problem is that, in TypeScript code, calling Object.defineProperty() in a constructor does not convince the compiler that the property in question is definitely assigned, so when you use the --strict compiler option, you'd need something like a definite assignment assertion to quiet the warnings:
class ColorPalette {
foregroundColor!: string; // declared and asserted
backgroundColor!: string; // ditto
borderColor!: string; // ditto
// ...
There's a suggestion at microsoft/TypeScript#42919 for the compiler to recognize Object.defineProperty() as initializing properties, but for now, it's also not part of the language.
If you're willing to write the name and type of each property twice, then you can get your code to work way you originally had it. If you aren't, then you need to do something else.
One possible way forward is to make a class factory function that produces class constructors from some input. You put some property descriptors (well, functions that return such descriptors) into the function, and out comes a class constructor which sets those properties. It could look like this:
function ClassFromDescriptorFactories<T extends object>(descriptors: ThisType<T> &
{ [K in keyof T]: (this: T) => TypedPropertyDescriptor<T[K]> }): new () => T {
return class {
constructor() {
let k: keyof T;
for (k in descriptors) {
Object.defineProperty(this, k, (descriptors as any)[k].call(this))
}
}
} as any;
}
The call signature means: for any generic T of an object type, you can pass in a descriptors object whose keys are from T, and whose properties are zero-arg functions that produce property descriptors for each property from T; and what comes out of the factory function has a construct signature that produces instances of type T.
The ThisType<T> is not necessary, but helps with inference in the case where you are defining descriptors in terms of other class properties. More below:
The implementation iterates through all the keys of descriptors when the constructor is called, and for each such key k, it defines a property on this with the key k, and the property descriptor that comes out when you call descriptors[k].
Note that the compiler cannot verify that the implementation matches the call signature, for the same reasons that it cannot verify your original example; we have not declared the properties and Object.defineProperty() has not been seen to initialize them. That's why the returned class has been asserted as any. This suppresses any warnings about the class implementation, so we have to be careful that the implementation and call signature match.
But anyway, once we have ClassFromDescriptorFactories(), we can use it multiple times.
For your color palette example, you can make a general-purpose colorDescriptor() function which takes an initValue string input, and which produces a no-arg function which produces a property descriptor with the validation you want:
function colorDescriptor(initValue: string) {
return () => {
let value = initValue;
return {
enumerable: true,
get() { return value },
set(hex: string) {
if (/^#[0-9a-f]{6}$/i.test(hex)) {
value = hex;
}
}
}
}
}
The value variable is used to store the actual string value of the color. The whole point of the indirection () => {...} is so that each instance of the eventual class has its own value variable; Otherwise, you'd end up having value be a static property of the class, which you don't want.
And now we can use it with ClassFromDescriptorFactories() to define ColorPalette:
class ColorPalette extends ClassFromDescriptorFactories({
foregroundColor: colorDescriptor("#cccccc"),
backgroundColor: colorDescriptor("#333333"),
borderColor: colorDescriptor("#aaaaaa"),
}) {
toString(): string {
return Object.values(this).map(c => c.toString().toUpperCase()).join(", ");
}
}
This compiles without error, and the compiler recognizes instances of ColorPalette as having string-valued properties at keys foregroundColor, backgroundColor, and borderColor, and at runtime these properties have the proper validation:
let redPalette = new ColorPalette();
redPalette.foregroundColor = "#ff0000";
console.log(redPalette.toString()); // "#FF0000, #333333, #AAAAAA"
redPalette.backgroundColor = "oopsie";
console.log(redPalette.backgroundColor) // still #333333
And just to make sure each instance has its own properties, let's create a new instance:
let bluePalette = new ColorPalette();
bluePalette.foregroundColor = "#0000ff";
console.log(redPalette.foregroundColor) // #ff0000
console.log(bluePalette.foregroundColor) // #0000ff
Yeah, bluePalette and redPalette don't share a common foregroundColor property. Looks good!
Note the ThisType<T> code comes in handy in situations like this, where we are adding a new descriptor that refers to other properties of the class:
class ColorPalette extends ClassFromDescriptorFactories({
foregroundColor: colorDescriptor("#cccccc"),
backgroundColor: colorDescriptor("#333333"),
borderColor: colorDescriptor("#aaaaaa"),
foregroundColorNumber() {
const that = this;
const descriptor = {
get() {
return Number.parseInt(that.foregroundColor.slice(1), 16);
}
}
return descriptor;
}
}) {
toString(): string {
return Object.values(this).map(c => c.toString().toUpperCase()).join(", ");
}
}
Here the compiler understands that foregroundColorNumber() is defining a number property on T, and that the this inside the implementation corresponds to T, and thus calls like the following work without error:
console.log("0x" + redPalette.foregroundColorNumber.toString(16)) // 0xff0000
console.log(redPalette.foregroundColor = "#000000")
console.log("0x" + redPalette.foregroundColorNumber.toString(16)) // 0x0
If you remove ThisType<T>, you will see some errors show up.
Playground link to code

How to check the interface type of an object? [duplicate]

This question is the direct analogon to Class type check with TypeScript
I need to find out at runtime if a variable of type any implements an interface. Here's my code:
interface A{
member:string;
}
var a:any={member:"foobar"};
if(a instanceof A) alert(a.member);
If you enter this code in the typescript playground, the last line will be marked as an error, "The name A does not exist in the current scope". But that isn't true, the name does exist in the current scope. I can even change the variable declaration to var a:A={member:"foobar"}; without complaints from the editor. After browsing the web and finding the other question on SO I changed the interface to a class but then I can't use object literals to create instances.
I wondered how the type A could vanish like that but a look at the generated javascript explains the problem:
var a = {
member: "foobar"
};
if(a instanceof A) {
alert(a.member);
}
There is no representation of A as an interface, therefore no runtime type checks are possible.
I understand that javascript as a dynamic language has no concept of interfaces. Is there any way to type check for interfaces?
The typescript playground's autocompletion reveals that typescript even offers a method implements. How can I use it ?
You can achieve what you want without the instanceof keyword as you can write custom type guards now:
interface A {
member: string;
}
function instanceOfA(object: any): object is A {
return 'member' in object;
}
var a: any = {member: "foobar"};
if (instanceOfA(a)) {
alert(a.member);
}
Lots of Members
If you need to check a lot of members to determine whether an object matches your type, you could instead add a discriminator. The below is the most basic example, and requires you to manage your own discriminators... you'd need to get deeper into the patterns to ensure you avoid duplicate discriminators.
interface A {
discriminator: 'I-AM-A';
member: string;
}
function instanceOfA(object: any): object is A {
return object.discriminator === 'I-AM-A';
}
var a: any = {discriminator: 'I-AM-A', member: "foobar"};
if (instanceOfA(a)) {
alert(a.member);
}
In TypeScript 1.6, user-defined type guard will do the job.
interface Foo {
fooProperty: string;
}
interface Bar {
barProperty: string;
}
function isFoo(object: any): object is Foo {
return 'fooProperty' in object;
}
let object: Foo | Bar;
if (isFoo(object)) {
// `object` has type `Foo`.
object.fooProperty;
} else {
// `object` has type `Bar`.
object.barProperty;
}
And just as Joe Yang mentioned: since TypeScript 2.0, you can even take the advantage of tagged union type.
interface Foo {
type: 'foo';
fooProperty: string;
}
interface Bar {
type: 'bar';
barProperty: number;
}
let object: Foo | Bar;
// You will see errors if `strictNullChecks` is enabled.
if (object.type === 'foo') {
// object has type `Foo`.
object.fooProperty;
} else {
// object has type `Bar`.
object.barProperty;
}
And it works with switch too.
How about User-Defined Type Guards? https://www.typescriptlang.org/docs/handbook/advanced-types.html
interface Bird {
fly();
layEggs();
}
interface Fish {
swim();
layEggs();
}
function isFish(pet: Fish | Bird): pet is Fish { //magic happens here
return (<Fish>pet).swim !== undefined;
}
// Both calls to 'swim' and 'fly' are now okay.
if (isFish(pet)) {
pet.swim();
}
else {
pet.fly();
}
typescript 2.0 introduce tagged union
Typescript 2.0 features
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
function area(s: Shape) {
// In the following switch statement, the type of s is narrowed in each case clause
// according to the value of the discriminant property, thus allowing the other properties
// of that variant to be accessed without a type assertion.
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.width * s.height;
case "circle": return Math.PI * s.radius * s.radius;
}
}
It's now possible, I just released an enhanced version of the TypeScript compiler that provides full reflection capabilities. You can instantiate classes from their metadata objects, retrieve metadata from class constructors and inspect interface/classes at runtime. You can check it out here
Usage example:
In one of your typescript files, create an interface and a class that implements it like the following:
interface MyInterface {
doSomething(what: string): number;
}
class MyClass implements MyInterface {
counter = 0;
doSomething(what: string): number {
console.log('Doing ' + what);
return this.counter++;
}
}
now let's print some the list of implemented interfaces.
for (let classInterface of MyClass.getClass().implements) {
console.log('Implemented interface: ' + classInterface.name)
}
compile with reflec-ts and launch it:
$ node main.js
Implemented interface: MyInterface
Member name: counter - member kind: number
Member name: doSomething - member kind: function
See reflection.d.ts for Interface meta-type details.
UPDATE:
You can find a full working example here
Type guards in Typescript:
TS has type guards for this purpose. They define it in the following manner:
Some expression that performs a runtime check that guarantees the type
in some scope.
This basically means that the TS compiler can narrow down the type to a more specific type when it has sufficient information. For example:
function foo (arg: number | string) {
if (typeof arg === 'number') {
// fine, type number has toFixed method
arg.toFixed()
} else {
// Property 'toFixed' does not exist on type 'string'. Did you mean 'fixed'?
arg.toFixed()
// TSC can infer that the type is string because
// the possibility of type number is eliminated at the if statement
}
}
To come back to your question, we can also apply this concept of type guards to objects in order to determine their type. To define a type guard for objects, we need to define a function whose return type is a type predicate. For example:
interface Dog {
bark: () => void;
}
// The function isDog is a user defined type guard
// the return type: 'pet is Dog' is a type predicate,
// it determines whether the object is a Dog
function isDog(pet: object): pet is Dog {
return (pet as Dog).bark !== undefined;
}
const dog: any = {bark: () => {console.log('woof')}};
if (isDog(dog)) {
// TS now knows that objects within this if statement are always type Dog
// This is because the type guard isDog narrowed down the type to Dog
dog.bark();
}
Here's another option: the module ts-interface-builder provides a build-time tool that converts a TypeScript interface into a runtime descriptor, and ts-interface-checker can check if an object satisfies it.
For OP's example,
interface A {
member: string;
}
You'd first run ts-interface-builder which produces a new concise file with a descriptor, say, foo-ti.ts, which you can use like this:
import fooDesc from './foo-ti.ts';
import {createCheckers} from "ts-interface-checker";
const {A} = createCheckers(fooDesc);
A.check({member: "hello"}); // OK
A.check({member: 17}); // Fails with ".member is not a string"
You can create a one-liner type-guard function:
function isA(value: any): value is A { return A.test(value); }
I would like to point out that TypeScript does not provide a direct mechanism for dynamically testing whether an object implements a particular interface.
Instead, TypeScript code can use the JavaScript technique of checking whether an appropriate set of members are present on the object. For example:
var obj : any = new Foo();
if (obj.someInterfaceMethod) {
...
}
same as above where user-defined guards were used but this time with an arrow function predicate
interface A {
member:string;
}
const check = (p: any): p is A => p.hasOwnProperty('member');
var foo: any = { member: "foobar" };
if (check(foo))
alert(foo.member);
In my opinion this is the best approach; attach a "Fubber" symbol to the interfaces. It is MUCH faster to write, MUCH faster for the JavaScript engine than a type guard, supports inheritance for interfaces and makes type guards easy to write if you need them.
This is the purpose for which ES6 has symbols.
Interface
// Notice there is no naming conflict, because interfaces are a *type*
export const IAnimal = Symbol("IAnimal");
export interface IAnimal {
[IAnimal]: boolean; // the fubber
}
export const IDog = Symbol("IDog");
export interface IDog extends IAnimal {
[IDog]: boolean;
}
export const IHound = Symbol("IDog");
export interface IHound extends IDog {
// The fubber can also be typed as only 'true'; meaning it can't be disabled.
[IDog]: true;
[IHound]: boolean;
}
Class
import { IDog, IAnimal } from './interfaces';
class Dog implements IDog {
// Multiple fubbers to handle inheritance:
[IAnimal] = true;
[IDog] = true;
}
class Hound extends Dog implements IHound {
[IHound] = true;
}
Testing
This code can be put in a type guard if you want to help the TypeScript compiler.
import { IDog, IAnimal } from './interfaces';
let dog = new Dog();
if (dog instanceof Hound || dog[IHound]) {
// false
}
if (dog[IAnimal]?) {
// true
}
let houndDog = new Hound();
if (houndDog[IDog]) {
// true
}
if (dog[IDog]?) {
// it definitely is a dog
}
TypeGuards
interface MyInterfaced {
x: number
}
function isMyInterfaced(arg: any): arg is MyInterfaced {
return arg.x !== undefined;
}
if (isMyInterfaced(obj)) {
(obj as MyInterfaced ).x;
}
Approaching 9 years since OP, and this problem remains. I really REALLY want to love Typescript. And usually I succeed. But its loopholes in type safety is a foul odor that my pinched nose can't block.
My goto solutions aren't perfect. But my opinion is they are better than most of the more commonly prescribed solutions. Discriminators have proven to be a bad practice because they limit scalability and defeat the purpose of type safety altogether. My 2 prettiest butt-ugly solutions are, in order:
Class Decorator:
Recursively scans the typed object's members and computes a hash based on the symbol names. Associates the hash with the type name in a static KVP property. Include the type name in the hash calculation to mitigate risk of ambiguity with ancestors (happens with empty subclasses).
Pros: It's proven to be the most trustworthy. It is also provides very strict enforcements. This is also similar to how other high-level languages natively implement polymorphism. Howbeit, the solution requires much further extension in order to be truly polymorphic.
Cons: Anonymous/JSON objects have to be rehashed with every type check, since they have no type definitions to associate and statically cache. Excessive stack overhead results in significant performance bottlenecks in high load scenarios. Can be mitigated with IoC containers, but that can also be undesirable overhead for small apps with no other rationale. Also requires extra diligence to apply the decorator to every object requiring it.
Cloning:
Very ugly, but can be beneficial with thoughtful strategies. Create a new instance of the typed object and reflexively copy the top-level member assignments from the anonymous object. Given a predetermined standard for passage, you can simultaneously check and clone-cast to types. Something akin to "tryParse" from other languages.
Pros: In certain scenarios, resource overhead can be mitigated by immediately using the converted "test" instance. No additional diligence required for decorators. Large amount of flexibility tolerances.
Cons: Memory leaks like a flour sifter. Without a "deep" clone, mutated references can break other components not anticipating the breach of encapsulation. Static caching not applicable, so operations are executed on each and every call--objects with high quantities of top-level members will impact performance. Developers who are new to Typescript will mistake you for a junior due to not understanding why you've written this kind of pattern.
All totalled: I don't buy the "JS doesn't support it" excuse for Typescript's nuances in polymorphism. Transpilers are absolutely appropriate for that purpose. To treat the wounds with salt: it comes from Microsoft. They've solved this same problem many years ago with great success: .Net Framework offered a robust Interop API for adopting backwards compatibility with COM and ActiveX. They didn't try to transpile to the older runtimes. That solution would have been much easier and less messy for a loose and interpreted language like JS...yet they cowered out with the fear of losing ground to other supersets. Using the very shortcomings in JS that was meant to be solved by TS, as a malformed basis for redefining static typed Object-Oriented principle is--well--nonsense. It smacks against the volumes of industry-leading documentation and specifications which have informed high-level software development for decades.
Based on Fenton's answer, here's my implementation of a function to verify if a given object has the keys an interface has, both fully or partially.
Depending on your use case, you may also need to check the types of each of the interface's properties. The code below doesn't do that.
function implementsTKeys<T>(obj: any, keys: (keyof T)[]): obj is T {
if (!obj || !Array.isArray(keys)) {
return false;
}
const implementKeys = keys.reduce((impl, key) => impl && key in obj, true);
return implementKeys;
}
Example of usage:
interface A {
propOfA: string;
methodOfA: Function;
}
let objectA: any = { propOfA: '' };
// Check if objectA partially implements A
let implementsA = implementsTKeys<A>(objectA, ['propOfA']);
console.log(implementsA); // true
objectA.methodOfA = () => true;
// Check if objectA fully implements A
implementsA = implementsTKeys<A>(objectA, ['propOfA', 'methodOfA']);
console.log(implementsA); // true
objectA = {};
// Check again if objectA fully implements A
implementsA = implementsTKeys<A>(objectA, ['propOfA', 'methodOfA']);
console.log(implementsA); // false, as objectA now is an empty object
I found an example from #progress/kendo-data-query in file filter-descriptor.interface.d.ts
Checker
declare const isCompositeFilterDescriptor: (source: FilterDescriptor | CompositeFilterDescriptor) => source is CompositeFilterDescriptor;
Example usage
const filters: Array<FilterDescriptor | CompositeFilterDescriptor> = filter.filters;
filters.forEach((element: FilterDescriptor | CompositeFilterDescriptor) => {
if (isCompositeFilterDescriptor(element)) {
// element type is CompositeFilterDescriptor
} else {
// element type is FilterDescriptor
}
});
You can validate a TypeScript type at runtime using ts-validate-type, like so (does require a Babel plugin though):
const user = validateType<{ name: string }>(data);
I knew I'd stumbled across a github package that addressed this properly, and after trawling through my search history I finally found it. Check out typescript-is - though it requires your code to be compiled using ttypescript (I am currently in the process of bullying it into working with create-react-app, will update on the success/failure later), you can do all sorts of crazy things with it. The package is also actively maintained, unlike ts-validate-type.
You can check if something is a string or number and use it as such, without the compiler complaining:
import { is } from 'typescript-is';
const wildString: any = 'a string, but nobody knows at compile time, because it is cast to `any`';
if (is<string>(wildString)) { // returns true
// wildString can be used as string!
} else {
// never gets to this branch
}
if (is<number>(wildString)) { // returns false
// never gets to this branch
} else {
// Now you know that wildString is not a number!
}
You can also check your own interfaces:
import { is } from 'typescript-is';
interface MyInterface {
someObject: string;
without: string;
}
const foreignObject: any = { someObject: 'obtained from the wild', without: 'type safety' };
if (is<MyInterface>(foreignObject)) { // returns true
const someObject = foreignObject.someObject; // type: string
const without = foreignObject.without; // type: string
}
Type guards in Typescript using Reflect
Here is an example of a type guard from my Typescript game engine
export interface Start {
/**
* Start is called on the frame when a script is enabled just before any of the Update methods are called the first time.
*/
start(): void
}
/**
* User Defined Type Guard for Start
*/
export const implementsStart = (arg: any): arg is Start => {
return Reflect.has(arg, 'start')
}
/**
* Example usage of the type guard
*/
start() {
this.components.forEach(component => {
if (implementsStart(component)) {
component.start()
}
})
}
export interface ConfSteps {
group: string;
key: string;
steps: string[];
}
private verify(): void {
const obj = `{
"group": "group",
"key": "key",
"steps": [],
"stepsPlus": []
} `;
if (this.implementsObject<ConfSteps>(obj, ['group', 'key', 'steps'])) {
console.log(`Implements ConfSteps: ${obj}`);
}
}
private objProperties: Array<string> = [];
private implementsObject<T>(obj: any, keys: (keyof T)[]): boolean {
JSON.parse(JSON.stringify(obj), (key, value) => {
this.objProperties.push(key);
});
for (const key of keys) {
if (!this.objProperties.includes(key.toString())) {
return false;
}
}
this.objProperties = null;
return true;
}
Another solution could be something similar what is used in case of HTMLIFrameElement interface. We can declare a variable with the same name by creating an object by the interface if we know that there is an implementation for it in another module.
declare var HTMLIFrameElement: {
prototype: HTMLIFrameElement;
new(): HTMLIFrameElement;
};
So in this situation
interface A {
member:string;
}
declare var A : {
prototype: A;
new(): A;
};
if(a instanceof A) alert(a.member);
should work fine
This answer is very simple. However, this solution is at least possible (though not always ideal) in maybe 3/4 of the cases. So, in other words, this is probably relevant to whomever is reading this.
Let's say I have a very simple function that needs to know a parameter's interface type:
const simpleFunction = (canBeTwoInterfaces: interfaceA | interface B) => {
// if interfaceA, then return canBeTwoInterfaces.A
// if interfaceB, then return canBeTwoInterfaces.B
}
The answers that are getting the most upvotes tend to be using "function checking". i.e.,
const simpleFunction = (canBeTwoInterfaces: interfaceA | interface B) => {
if (canBeTwoInterfaces.onlyExistsOnInterfaceA) return canBeTwoInterfaces.A
else return canBeTwoInterfaces.B
}
However, in the codebase I'm working with, the interfaces I'm needing to check mostly consist optional parameters. Plus, someone else on my team might suddently change the names names without me knowing. If this sounds like the codebase you're working in, then the function below is much safer.
Like I said earlier, this might strike many as being a very obvious thing to do. Nonetheless, it is not obvious to know when and where to apply a given solution, regardless of whether it happens to be a brutally simple one like below.
This is what I would do:
const simpleFunction = (
canBeTwoInterfaces: interfaceA | interface B,
whichInterfaceIsIt: 'interfaceA' | 'interfaceB'
) => {
if (whichInterfaceIsIt === 'interfaceA') return canBeTwoInterface.A
else return canBeTwoInterfaces.B
}
You can also send multiple inputs to child components, having one be a discriminator, and the other being the actual data, and checking the discriminator in the child component like this:
#Input() data?: any;
#Input() discriminator?: string;
ngOnInit(){
if(this.discriminator = 'InterfaceAName'){
//do stuff
}
else if(this.discriminator = 'InterfaceBName'){
//do stuff
}
}
Obviously you can move this into wherever it is applicable to use, like an ngOnChanges function or a setter function, but the idea still stands. I would also recommend trying to tie an ngModel to the input data if you want a reactive form. You can use these if statements to set the ngModel based on the data being passed in, and reflect that in the html with either:
<div [(ngModel)]={{dataModel}}>
<div *ngFor="let attr of (data | keyvalue)">
<!--You can use attr.key and attr.value in this situation to display the attributes of your interface, and their associated values from the data -->
</div>
</div>
Or This Instead:
<div *ngIf = "model == 'InterfaceAName'">
<div>Do This Stuff</div>
</div>
<div *ngIf= "model == 'IntefaceBName'">
<div>Do this instead</div>
</div>
(You can use attr.key and attr.value in this situation to display the attributes of your interface, and their associated values from the data)
I know the question is already answered, but I thought this might be useful for people trying to build semi-ambiguous angular forms. You can also use this for angular material modules (dialog boxes for example), by sending in two variables through the data parameter--one being your actual data, and the other being a discriminator, and checking it through a similar process. Ultimately, this would allow you to create one form, and shape the form around the data being flowed into it.
Working with string literals is difficult because if you want to refactor you method or interface names then it could be possible that your IDE don't refactor these string literals.
I provide you mine solution which works if there is at least one method in the interface
export class SomeObject implements interfaceA {
public methodFromA() {}
}
export interface interfaceA {
methodFromA();
}
Check if object is of type interface:
const obj = new SomeObject();
const objAsAny = obj as any;
const objAsInterfaceA = objAsAny as interfaceA;
const isObjOfTypeInterfaceA = objAsInterfaceA.methodFromA != null;
console.log(isObjOfTypeInterfaceA)
Note: We will get true even if we remove 'implements interfaceA' because the method still exists in the SomeObject class
Simple workaround solution having the same drawbacks as the selected solution, but this variant catches JS errors, only accepts objects as parameter, and has a meaningful return value.
interface A{
member:string;
}
const implementsA = (o: object): boolean => {
try {
return 'member' in o;
} catch (error) {
return false;
}
}
const a:any={member:"foobar"};
implementsA(a) && console.log("a implements A");
// implementsA("str"); // causes TS transpiler error
I know the question is a bit old, but just my 50 cents. This worked for me:
const container: Container = icc.controlComponent as unknown as Container;
if (container.getControlComponents) {
this.allControlComponents.push(...container.getControlComponents());
}
Container is the interface, and icc.controlComponent is the object I wanted to check, and getControlComponents is a method from Container interface.
Here's the solution I came up with using classes and lodash: (it works!)
// TypeChecks.ts
import _ from 'lodash';
export class BakedChecker {
private map: Map<string, string>;
public constructor(keys: string[], types: string[]) {
this.map = new Map<string, string>(keys.map((k, i) => {
return [k, types[i]];
}));
if (this.map.has('__optional'))
this.map.delete('__optional');
}
getBakedKeys() : string[] {
return Array.from(this.map.keys());
}
getBakedType(key: string) : string {
return this.map.has(key) ? this.map.get(key) : "notfound";
}
}
export interface ICheckerTemplate {
__optional?: any;
[propName: string]: any;
}
export function bakeChecker(template : ICheckerTemplate) : BakedChecker {
let keys = _.keysIn(template);
if ('__optional' in template) {
keys = keys.concat(_.keysIn(template.__optional).map(k => '?' + k));
}
return new BakedChecker(keys, keys.map(k => {
const path = k.startsWith('?') ? '__optional.' + k.substr(1) : k;
const val = _.get(template, path);
if (typeof val === 'object') return val;
return typeof val;
}));
}
export default function checkType<T>(obj: any, template: BakedChecker) : obj is T {
const o_keys = _.keysIn(obj);
const t_keys = _.difference(template.getBakedKeys(), ['__optional']);
return t_keys.every(tk => {
if (tk.startsWith('?')) {
const ak = tk.substr(1);
if (o_keys.includes(ak)) {
const tt = template.getBakedType(tk);
if (typeof tt === 'string')
return typeof _.get(obj, ak) === tt;
else {
return checkType<any>(_.get(obj, ak), tt);
}
}
return true;
}
else {
if (o_keys.includes(tk)) {
const tt = template.getBakedType(tk);
if (typeof tt === 'string')
return typeof _.get(obj, tk) === tt;
else {
return checkType<any>(_.get(obj, tk), tt);
}
}
return false;
}
});
}
custom classes:
// MyClasses.ts
import checkType, { bakeChecker } from './TypeChecks';
class Foo {
a?: string;
b: boolean;
c: number;
public static _checker = bakeChecker({
__optional: {
a: ""
},
b: false,
c: 0
});
}
class Bar {
my_string?: string;
another_string: string;
foo?: Foo;
public static _checker = bakeChecker({
__optional: {
my_string: "",
foo: Foo._checker
},
another_string: ""
});
}
to check the type at runtime:
if (checkType<Bar>(foreign_object, Bar._checker)) { ... }
Because the type is unknown at run-time, I wrote code as follows to compare the unknown object, not against a type, but against an object of known type:
Create a sample object of the right type
Specify which of its elements are optional
Do a deep compare of your unknown object against this sample object
Here's the (interface-agnostic) code I use for the deep compare:
function assertTypeT<T>(loaded: any, wanted: T, optional?: Set<string>): T {
// this is called recursively to compare each element
function assertType(found: any, wanted: any, keyNames?: string): void {
if (typeof wanted !== typeof found) {
throw new Error(`assertType expected ${typeof wanted} but found ${typeof found}`);
}
switch (typeof wanted) {
case "boolean":
case "number":
case "string":
return; // primitive value type -- done checking
case "object":
break; // more to check
case "undefined":
case "symbol":
case "function":
default:
throw new Error(`assertType does not support ${typeof wanted}`);
}
if (Array.isArray(wanted)) {
if (!Array.isArray(found)) {
throw new Error(`assertType expected an array but found ${found}`);
}
if (wanted.length === 1) {
// assume we want a homogenous array with all elements the same type
for (const element of found) {
assertType(element, wanted[0]);
}
} else {
// assume we want a tuple
if (found.length !== wanted.length) {
throw new Error(
`assertType expected tuple length ${wanted.length} found ${found.length}`);
}
for (let i = 0; i < wanted.length; ++i) {
assertType(found[i], wanted[i]);
}
}
return;
}
for (const key in wanted) {
const expectedKey = keyNames ? keyNames + "." + key : key;
if (typeof found[key] === 'undefined') {
if (!optional || !optional.has(expectedKey)) {
throw new Error(`assertType expected key ${expectedKey}`);
}
} else {
assertType(found[key], wanted[key], expectedKey);
}
}
}
assertType(loaded, wanted);
return loaded as T;
}
Below is an example of how I use it.
In this example I expect the JSON contains an array of tuples, of which the second element is an instance of an interface called User (which has two optional elements).
TypeScript's type-checking will ensure that my sample object is correct, then the assertTypeT function checks that the unknown (loaded from JSON) object matches the sample object.
export function loadUsers(): Map<number, User> {
const found = require("./users.json");
const sample: [number, User] = [
49942,
{
"name": "ChrisW",
"email": "example#example.com",
"gravatarHash": "75bfdecf63c3495489123fe9c0b833e1",
"profile": {
"location": "Normandy",
"aboutMe": "I wrote this!\n\nFurther details are to be supplied ..."
},
"favourites": []
}
];
const optional: Set<string> = new Set<string>(["profile.aboutMe", "profile.location"]);
const loaded: [number, User][] = assertTypeT(found, [sample], optional);
return new Map<number, User>(loaded);
}
You could invoke a check like this in the implementation of a user-defined type guard.

ES6 class with private/protected properties

I have a ES6 class as below
class CoffeeMachine {
constructor(power) {
this._power = power;
}
get power() {
return this._power;
}
}
// create the coffee machine
let coffeeMachine = new CoffeeMachine(100);
console.log(coffeeMachine);
I have 2 questions;
For the console.log(coffeeMachine), I get below;
CoffeeMachine {
_power: 100,
__proto__:
power: 100
}
I can see that "power" is on the proto of CoffeeMachine and also has the same value i.e. 100
How does that happen considering I am not setting "power" explicitly anywhere ?
Is it really possible to implement a private/protected in this example ? I know there is #privateVar in the latest ES, but wanted to know if it can be emulated in this code ?
getters and setters are transparent, meaning that an observer (the console) cannot distinguish between a regular property and a getter / setter.
How does that happen considering I am not setting "power" explicitly anywhere ?
I guess that's the console evaluates all values of the object once, then it associates those values to the respective object owning those properties. Otherwise it would have to evaluate the getter twice (once for the object and once for the prototype) and that behaviour is probably unwanted.
You can manually evaluate the getter on the prototype though:
class See { get me() { return "now"; } }
console.log(See.prototype.me);
Is it really possible to implement a private/protected in this example ?
Yes, using an IIFE around a Map that is certainly possible in ES6:
const Private = (() => {
const priv = new WeakMap();
return class {
get priv() { return priv.get(this); }
set priv(v) { priv.set(this, v); }
};
})();
But, as pointed out in the comments already, you probably don't need that. And for ES2020 there are private properties just as you said.
You need to create the private variable with a # prefix. Example:
class CoffeeMachine {
#_power;
constructor(power) {
this.#_power = power;
}
get power() {
return this.#_power;
}
}
// create the coffee machine
let coffeeMachine = new CoffeeMachine(100);
console.log(coffeeMachine);
console.log(coffeeMachine._power);
console.log(coffeeMachine.power);

Cannot call subclass function in Gnome Shell Extension ES6

I am trying to write a GNOME shell extension. I have a class which extends a GNOME shell type. Whenever I call class method on this subclass, it throws an error. If I do not extend the class, then the method is invoked without a problem.
Example:
This works
var Dummy = class Dummy {
constructor() {}
foo() {
log("foo!")
}
};
let d = new Dummy();
d.foo();
--> RESULT: log shows "foo!"
However, this does not work
const St = imports.gi.St;
var Dummy = class Dummy extends St.Bin {
constructor() {
super();
}
foo() {
log("foo!")
}
};
let d = new Dummy();
d.foo();
--> RESULT: TypeError: d.foo is not a function
I'm not very proficient in Javascript, and I'm having trouble Googling my way out of this situation.
Any help is appreciated. Thanks!
Unfortunately there is a bit of mix of Class styles, for couple reasons, but namely because GJS existed before ES6 classes. You should avoid usage of the Lang module if at all possible.
Sometimes it won't be clear if the object you're subclassing is a native JS class or a GObject. Any object coming from imports.gi (GObject Introspection) will be a GObject, while objects coming from say imports.ui.popupMenu (an import in GNOME Shell) could be either and you may have to check the source.
If you are subclassing a GObject, this is the proper way to subclass:
var Bin = GObject.registerClass({
// This is optional, but useful to avoid namespace collisions. By
// convention you prefix your GType's like StBin or GtkBin.
GTypeName: 'PohoBin',
// Custom GProperties
Properties: {
'example-prop': GObject.ParamSpec.string(
'example-prop', // property name
'ExampleProperty', // nickname
'An example read write property', // description
(GObject.ParamFlags.READWRITE | // READABLE/READWRITE/CONSTRUCT/etc
GObject.ParamFlags.CONSTRUCT),
null // implement defaults manually
)
}
}, class Bin extends St.Bin {
// Currently GObjects use `constructor()` for bootstrapping; you
// should not call this function in GObject subclasses.
//
// Instead, you should chain-up to the parent class's _init() function,
// which takes a dictionary of properties
_init(params = {}) {
// Chaining up. If you need to, you can use this opportunity to
// pull properties from the #params dictionary
super._init(params);
}
get example_prop() {
if (this._example_prop === undefined)
this._example_prop = null;
return this._example_prop;
}
set example_prop(value) {
if (this.example_prop !== value) {
this._example_prop = value;
this.notify('example-prop');
}
}
foo() {
log(this.example_prop);
}
});
let obj = new Bin({
visible: true, // Clutter.Actor property
x_align: St.Align.MIDDLE, // St.Bin property
example_prop: 'example' // custom property
});
obj.foo();
// expected output: 'example'
There is more information about mapping GObject to JavaScript here:
https://gitlab.gnome.org/GNOME/gjs/wikis/Mapping
There are also a number of more complete examples here:
https://gitlab.gnome.org/GNOME/gjs/tree/master/examples

How to assign a Function to a Function using TypeScript? [duplicate]

I want to create a function object, which also has some properties held on it. For example in JavaScript I would do:
var f = function() { }
f.someValue = 3;
Now in TypeScript I can describe the type of this as:
var f: { (): any; someValue: number; };
However I can't actually build it, without requiring a cast. Such as:
var f: { (): any; someValue: number; } =
<{ (): any; someValue: number; }>(
function() { }
);
f.someValue = 3;
How would you build this without a cast?
Update: This answer was the best solution in earlier versions of TypeScript, but there are better options available in newer versions (see other answers).
The accepted answer works and might be required in some situations, but have the downside of providing no type safety for building up the object. This technique will at least throw a type error if you attempt to add an undefined property.
interface F { (): any; someValue: number; }
var f = <F>function () { }
f.someValue = 3
// type error
f.notDeclard = 3
This is easily achievable now (typescript 2.x) with Object.assign(target, source)
example:
The magic here is that Object.assign<T, U>(t: T, u: U) is typed to return the intersection T & U.
Enforcing that this resolves to a known interface is also straight-forward. For example:
interface Foo {
(a: number, b: string): string[];
foo: string;
}
let method: Foo = Object.assign(
(a: number, b: string) => { return a * a; },
{ foo: 10 }
);
which errors due to incompatible typing:
Error: foo:number not assignable to foo:string
Error: number not assignable to string[] (return type)
caveat: you may need to polyfill Object.assign if targeting older browsers.
TypeScript is designed to handle this case through declaration merging:
you may also be familiar with JavaScript practice of creating a function and then extending the function further by adding properties onto the function. TypeScript uses declaration merging to build up definitions like this in a type-safe way.
Declaration merging lets us say that something is both a function and a namespace (internal module):
function f() { }
namespace f {
export var someValue = 3;
}
This preserves typing and lets us write both f() and f.someValue. When writing a .d.ts file for existing JavaScript code, use declare:
declare function f(): void;
declare namespace f {
export var someValue: number;
}
Adding properties to functions is often a confusing or unexpected pattern in TypeScript, so try to avoid it, but it can be necessary when using or converting older JS code. This is one of the only times it would be appropriate to mix internal modules (namespaces) with external.
So if the requirement is to simply build and assign that function to "f" without a cast, here is a possible solution:
var f: { (): any; someValue: number; };
f = (() => {
var _f : any = function () { };
_f.someValue = 3;
return _f;
})();
Essentially, it uses a self executing function literal to "construct" an object that will match that signature before the assignment is done. The only weirdness is that the inner declaration of the function needs to be of type 'any', otherwise the compiler cries that you're assigning to a property which does not exist on the object yet.
EDIT: Simplified the code a bit.
Old question, but for versions of TypeScript starting with 3.1, you can simply do the property assignment as you would in plain JS, as long as you use a function declaration or the const keyword for your variable:
function f () {}
f.someValue = 3; // fine
const g = function () {};
g.someValue = 3; // also fine
var h = function () {};
h.someValue = 3; // Error: "Property 'someValue' does not exist on type '() => void'"
Reference and online example.
As a shortcut, you can dynamically assign the object value using the ['property'] accessor:
var f = function() { }
f['someValue'] = 3;
This bypasses the type checking. However, it is pretty safe because you have to intentionally access the property the same way:
var val = f.someValue; // This won't work
var val = f['someValue']; // Yeah, I meant to do that
However, if you really want the type checking for the property value, this won't work.
I can't say that it's very straightforward but it's definitely possible:
interface Optional {
<T>(value?: T): OptionalMonad<T>;
empty(): OptionalMonad<any>;
}
const Optional = (<T>(value?: T) => OptionalCreator(value)) as Optional;
Optional.empty = () => OptionalCreator();
if you got curious this is from a gist of mine with the TypeScript/JavaScript version of Optional
An updated answer: since the addition of intersection types via &, it is possible to "merge" two inferred types on the fly.
Here's a general helper that reads the properties of some object from and copies them over an object onto. It returns the same object onto but with a new type that includes both sets of properties, so correctly describing the runtime behaviour:
function merge<T1, T2>(onto: T1, from: T2): T1 & T2 {
Object.keys(from).forEach(key => onto[key] = from[key]);
return onto as T1 & T2;
}
This low-level helper does still perform a type-assertion, but it is type-safe by design. With this helper in place, we have an operator that we can use to solve the OP's problem with full type safety:
interface Foo {
(message: string): void;
bar(count: number): void;
}
const foo: Foo = merge(
(message: string) => console.log(`message is ${message}`), {
bar(count: number) {
console.log(`bar was passed ${count}`)
}
}
);
Click here to try it out in the TypeScript Playground. Note that we have constrained foo to be of type Foo, so the result of merge has to be a complete Foo. So if you rename bar to bad then you get a type error.
NB There is still one type hole here, however. TypeScript doesn't provide a way to constrain a type parameter to be "not a function". So you could get confused and pass your function as the second argument to merge, and that wouldn't work. So until this can be declared, we have to catch it at runtime:
function merge<T1, T2>(onto: T1, from: T2): T1 & T2 {
if (typeof from !== "object" || from instanceof Array) {
throw new Error("merge: 'from' must be an ordinary object");
}
Object.keys(from).forEach(key => onto[key] = from[key]);
return onto as T1 & T2;
}
This departs from strong typing, but you can do
var f: any = function() { }
f.someValue = 3;
if you are trying to get around oppressive strong typing like I was when I found this question. Sadly this is a case TypeScript fails on perfectly valid JavaScript so you have to you tell TypeScript to back off.
"You JavaScript is perfectly valid TypeScript" evaluates to false. (Note: using 0.95)

Categories

Resources