Interfaces and Getters\Setters - javascript

I have the following:
interface IEngine {
type():string;
type(type:string):void;
}
class Engine implements IEngine {
private _type: string;
get type():string {
return this._type;
}
set type(type:string) {
this._type = type;
}
}
var engine = new Engine();
engine.type = 'foo';
The interface looks to me to be implemented, however, running tsc throws an exception:
F:\>tsc interfaces.ts --target "es5"
interfaces.ts(11,7): error TS2420: Class 'Engine' incorrectly implements interface 'IEngine'.
Types of property 'type' are incompatible.
Type 'string' is not assignable to type '{ (): string; (type: string): void; }'.

You are implementing property, so in interface it should be like this:
interface IEngine {
type:string;
}

Interfaces in Typescript are great for defining the "shape" of the object you want. In your example you are looking for an object that has a property called type. This can be done by specifying:
interface IEngine {
type: string;
}
The getters and setters are implementation details which are then defined in objects that implement the interface such the Engine type in your question.

Related

Argument of type 'X' is not assignable to parameter of type 'object'

I can not figure out what I am missing here. This works fine on the typescript playground, but not in my code. I have query build that builds graphql queries, but for some reason, I get an error that Argument of type 'QueryBuilder<CSVManager>' is not assignable to parameter of type 'QueryBuilder<object>'. Why is this not working?
export interface UserInfoDatabaseResult {
managers: CSVManager[];
users: CSVExistingUser[];
roles: CSVClientRole[];
}
#Injectable()
export class AddMultipleUsersService {
constructor(
private readonly queryBuilder: QueryBuilderService
) { }
getUsersInfo(managers: UserId[], users: UserId[], roles: [client: string, role: string][]) {
const managersQuery = this.getManagers(managers); // QueryBuilder<CSVManager>
const usersQuery = this.getExistingUsers(users); // QueryBuilder<CSVExistingUser>
const rolesQuery = this.getRoles(roles); // QueryBuilder<CSVClientRole>
return this.queryBuilder
.queryTransaction<UserInfoDatabaseResult>(managersQuery, usersQuery, rolesQuery);
}
// This simplest one looks like this:
getExistingUsers(users: UserId[]): QueryBuilder<CSVExistingUser> {
return this.queryBuilder
.table<CSVExistingUser>('user_tbl', 'users')
.select('id: user_id')
.where({ user_id: { _in: users } });
}
}
Here is how it is called:
#Injectable({ providedIn: 'root' })
export class QueryBuilderService {
public queryTransaction<Out = object, In = object>(...builders: QueryBuilder<In>[]): Observable<Out> {
return this.connection('default')
.queryTransaction<Out, In>(...builders);
}
}
This is then the error that I am receiving:
error TS2345: Argument of type 'QueryBuilder<CSVManager>' is not assignable to parameter of type 'QueryBuilder<object>'.
The types of 'paginationBehavior.observers' are incompatible between these types.
Type 'Observer<IPaginationResult<CSVManager>>[]' is not assignable to type 'Observer<IPaginationResult<object>>[]'.
Type 'Observer<IPaginationResult<CSVManager>>' is not assignable to type 'Observer<IPaginationResult<object>>'.
Type 'IPaginationResult<object>' is not assignable to type 'IPaginationResult<CSVManager>'.
42 .queryTransaction<UserInfoDatabaseResult>(managersQuery, usersQuery, rolesQuery);
~~~~~~~~~~~~~
As seen in the below image, the second parameter is unknown for some reason....
Edit
After some investigation, the issue is with this property:
export class QueryBuilder<OutMain extends object = object> {
private readonly paginationBehavior = new Subject<IPaginationResult<OutMain>>();
}
Changing it to the following removes the error, however it is not strongly typed due to the any keyword (which I don't want to use). Any thoughts on how to fix this?
private readonly paginationBehavior: Subject<IPaginationResult<OutMain>> & {observers: any} = new Subject<IPaginationResult<OutMain>>();

NDEFReader in TypeScript

I'm trying to use NDEFReader() for NFC scan/write in React.
This feature works from Chrome 81 (you can try it on your mobile in Chrome beta today on links below).
GoogleChromeNfcSample,
WhatWebCanDoTodayNfc
To enable this feature, you need to go into chrome://flags/ and enable Experimental Web Platform features.
The problem is that I can't make this work in React. I use create-react-app with TypeScript and console output:
Cannot find name 'NDEFReader'
I think that this causes a webpack check. I already tried change some settings in tsconfig.json but nothing worked. Does anyone know, how to enable experimental js/ts compilation, to enable this feature?
Web NFC folks provide TypeScript definitions at https://github.com/w3c/web-nfc/blob/gh-pages/web-nfc.d.ts
// Type definitions for Web NFC
// Project: https://github.com/w3c/web-nfc
// Definitions by: Takefumi Yoshii <https://github.com/takefumi-yoshii>
// TypeScript Version: 3.9
// This type definitions referenced to WebIDL.
// https://w3c.github.io/web-nfc/#actual-idl-index
interface Window {
NDEFMessage: NDEFMessage
}
declare class NDEFMessage {
constructor(messageInit: NDEFMessageInit)
records: ReadonlyArray<NDEFRecord>
}
declare interface NDEFMessageInit {
records: NDEFRecordInit[]
}
declare type NDEFRecordDataSource = string | BufferSource | NDEFMessageInit
interface Window {
NDEFRecord: NDEFRecord
}
declare class NDEFRecord {
constructor(recordInit: NDEFRecordInit)
readonly recordType: string
readonly mediaType?: string
readonly id?: string
readonly data?: DataView
readonly encoding?: string
readonly lang?: string
toRecords?: () => NDEFRecord[]
}
declare interface NDEFRecordInit {
recordType: string
mediaType?: string
id?: string
encoding?: string
lang?: string
data?: NDEFRecordDataSource
}
declare type NDEFMessageSource = string | BufferSource | NDEFMessageInit
interface Window {
NDEFReader: NDEFReader
}
declare class NDEFReader extends EventTarget {
constructor()
onreading: (this: this, event: NDEFReadingEvent) => any
onreadingerror: (this: this, error: Event) => any
scan: (options?: NDEFScanOptions) => Promise<void>
write: (
message: NDEFMessageSource,
options?: NDEFWriteOptions
) => Promise<void>
}
interface Window {
NDEFReadingEvent: NDEFReadingEvent
}
declare class NDEFReadingEvent extends Event {
constructor(type: string, readingEventInitDict: NDEFReadingEventInit)
serialNumber: string
message: NDEFMessage
}
interface NDEFReadingEventInit extends EventInit {
serialNumber?: string
message: NDEFMessageInit
}
interface NDEFWriteOptions {
overwrite?: boolean
signal?: AbortSignal
}
interface NDEFScanOptions {
signal: AbortSignal
}
This isn't about Webpack checks or your tsconfig, or "experimental JavaScript".
It's just that there are no types for NDEFReader() available, so TypeScript thinks you have a typo.
You can stub in a type for NDEFReader with a file like extra-globals.d.ts (the name doesn't matter so long as it's a .d.ts) in your source tree. This basically tells TypeScript that the global Window interface has one extra field, NDEFReader, whose type you don't really know:
declare global {
interface Window {
NDEFReader: any;
}
}
export {};

Typescript implementing interface property

I have declared interface as below
interface Base {
required: string;
}
I have implemented interface in class like
class MyClass implements Base{
method(): void {
console.log(this.required);
}
}
But I am getting following error
severity: 'Error' message: 'Class 'MyClass' incorrectly implements
interface 'Base'. Property 'required' is missing in type 'MyClass'.'
at: '5,7' source: 'ts'
severity: 'Error' message: 'Property 'required' does not exist on type
'MyClass'.' at: '7,26' source: 'ts'
if I declare required: string; once again in class then no error
interface Base {
required: string;
}
class MyClass implements Base{
required: string;
method(): void {
this.required="ddd";
console.log(this.required);
// you can access HTMLElement
}
}
var ss=new MyClass();
ss.method();
If you don't want to delcare requried: string twice use class instate interface for Base and extends instate implements.
class Base {
required: string;
}
class MyClass extends Base{
method(): void {
this.required="ddd";
console.log(this.required);
// you can access HTMLElement
}
}
Check it out in the playground.
That's how interfaces work. If you define a property in the interface then you need to define the same property in the class where you are implementing the interface. If you would like to use required property without re define the property, you should create a class an extend it.
Your error is correct. If your class implements an interface, it must also implement all the required properties and methods. If you want not to implement some properties or methods, you can declare them as optional with ? symbol.
interface Base {
required: string;
someProperty?: string; // << look at the symbol `?` in the end of the property
}
Here you can implement the interface and left the someProperty
class MyClass implements Base{
required: string;
// someProperty is missing here, because it is optional
method(): void {
this.required="ddd";
console.log(this.required);
// you can access HTMLElement
}
}
And not only you can implement interfaces. Also you can use them as a type. If you have an interface
interface Base {
required: string;
}
you can create an object which is the type of that interface
const obj: Base = { };
But here you will get an error because if your object is of type Base, you need to provide all required properties. So you need to write
const obj: Base = { required: 'Yes' };
This will protect your code from logical errors and your code will be strong typed also for object, for which you don't want to create a class, but you want to said what shape it must be.
Example
You have an interface
interface Name {
name: string
}
and have classes
class Car implements Name {
name: string;
engine: string
constructor(name: string, engine: string){
this.name = name;
this.engine = engine;
}
}
class Person implements Name {
name: string;
surname: string;
constructor(name: string, surname: string){
this.name = name;
this.surname = surname;
}
}
var arr: Name = [new Car('Car', 'JZ'), new Person('Name', 'Surname')];
here arr is an array of type Name. So if you get arr[0], and call on it .engine, intelisense will throw an error that there is no engine property in type Name. But you can be sure, that every object in that array has name property, because the type of that array is Name and it has an required property name.

How do I declare lambda-style function name property in TypeScript?

I am using TypeScript 2.0 in a Node project. Functions in Node have a name property. I have tried extending the TypeScript Function interface to declare the property like so:
interface Function
{
name: string
}
interface IAsyncInfo {
name: string,
args: any[],
func: (...args: any[]) => any
}
var info: IAsyncInfo;
var name = info.func.name
But TypeScript still complains on the last line:
Property 'name' does not exist on type (...args: any[]) => any
Then I tried:
var name = (info.func as Function).name
But TypeScript complains
type '(...args: any[]) => any' cannot be converted to type 'Function'
I don't really know what you are trying.
Do you want something like this?
interface IAsyncInfo {
name: string;
args: any[];
func(...args: any[]): any;
}
var info: IAsyncInfo;
var test = info.func.name;

Difference between the static and instance sides of classes

I am trying to understand interface topic in Typescript
when I came across Class type, I got this code from official docs
interface ClockConstructor {
new (hour: number, minute: number);
}
class Clock implements ClockConstructor {
currentTime: Date;
constructor(h: number, m: number) { }
}
I can understand that Clock has no match for the signature new (hour: number, minute: number); that's why we get an error there.
But in docs the explaination is something which I am unable to understand. It goes in this way :
This is because when a class implements an interface, only the instance side of the class is checked. Since the constructor sits in the static side, it is not included in this check.
Any explanation would be appreciated.
The interface declares the method/members that the instances have, and not what the implementing class has.
For example check the Array and ArrayConstructor declarations:
interface Array<T> {
length: number;
toString(): string;
toLocaleString(): string;
push(...items: T[]): number;
pop(): T | undefined;
...
[n: number]: T;
}
interface ArrayConstructor {
new (arrayLength?: number): any[];
new <T>(arrayLength: number): T[];
new <T>(...items: T[]): T[];
(arrayLength?: number): any[];
<T>(arrayLength: number): T[];
<T>(...items: T[]): T[];
isArray(arg: any): arg is Array<any>;
readonly prototype: Array<any>;
}
As you can see, the Array has method/members which exist on any instance of array:
let a = [];
a.push(1, 2, 3);
console.log(a.length);
But the ArrayConstructor has the members/methods which exist on the Array itself:
console.log(Array. prototype);
console.log(Array.isArray(9));
The constructors are part of the "static" part which is why they are declared in the ArrayConstructor.
If you declare a constructor on an interface for example you'll have a problem implementing that interface:
interface MyInterface {
constructor();
getName(): string;
}
class MyClass implements MyInterface {
constructor() {}
getName() { return "name" };
}
Error:
Class 'MyClass' incorrectly implements interface 'MyInterface'. Types
of property 'constructor' are incompatible. Type 'Function' is not
assignable to type '() => void'. Type 'Function' provides no match for
the signature '(): any'.
Before you can get an instance you need to use the static side, the constructor, to get an instance. You don't need new in your interface anyway, your class is typed itself so typescript knows whatever arguments it has to pass along the constructor.
You can make benefits of an interface with type new if you want to pass a function or class that has to meet certain constructor requirements before it can be instantiated.
interface IFoo {
new(title: string);
}
function MyFunction(ctor: IFoo, title:string) {
return new ctor(title);
}
class MyClass {
constructor(public title: string) {}
}
class MySecondClass {
constructor(public title: string) {}
}
var myClass = MyFunction(MyClass, 'title');
var mySecondClass = MyFunction(MySecondClass, 'title');
console.log(myClass.title, mySecondClass.title);
In fact, a TypeScript class is a regular function in JavaScript which is static when you don't use new in front of it. This is were the docs are referring to.
// static side
function Person() {
}
Person.SayHi = function () {
return 'Hello';
}
console.log(Person.SayHi()); // static function..
var person = new Person() // instance side
See also this answer

Categories

Resources