Psa Interface Checker 4.3 4

21 views
Skip to first unread message

Frida Kosofsky

unread,
Jul 24, 2024, 7:04:02 AM7/24/24
to inisexkraf

This package is the runtime support for validators created byts-interface-builder.It allows validating data, such as parsed JSON objects receivedover the network, or parsed JSON or YAML files, to check if they satisfy aTypeScript interface, and to produce informative error messages if they do not.

psa interface checker 4.3 4


Download Filehttps://urlin.us/2zJDze



Note that ts-interface-builder is only needed for the build-time step, andts-interface-checker is needed at runtime. That's why the recommendation is to npm-install theformer using --save-dev flag and the latter using --save.

You may check that data contains no extra properties. Note that it is not generally recommended asit this prevents backward compatibility: if you add new properties to an interface, then oldercode with strict checks will not accept them.

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.

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.

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

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:

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.

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.

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.

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.

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.

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.

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:

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.

The premise of the question is that interface should provide runtime identification of what objects implement a given interface. But that's not how Interface has been designed in TypeScript because, as both the OP and others have said, Interfaces simply do not exist at runtime. The implications of that design choice is that at runtime, interfaces are no more than a collection of JavaScript object properties.

This is essentially what a lot of the proposed workarounds, including type guards, boil down to. This has obvious limitation when there are multiple members in the interface (should I check for them all? should I define an interface identifier/discriminator property?) and will in some cases require additional type checks on the members to pass the TS compiler's type checker. (E.g., another interface might implement member as number).

If the idea is to allow objects to exhibit behavior depending on what interfaces are present, then a better pattern is to use a base interface with a common method (or methods) to trigger behavior and then extend the base interface to add specialized properties and/or methods. Extending the OPs example:

The point being that implementations of either interface are forced to implement a talk method with a common spec, which is enforced by the type checker. The compiler's type checking becomes increasingly valuable as the interfaces grow in complexity.

The biggest weakness of TypeScript's Interface is the lack of default implementations. You can fake it by defining a default object implementation of the interface and copying from it, like var defaultA = talk() 'no member'; var a = ...defaultA, but it would be much cleaner if it was possible to specify the defaults in the interface definition.

With many objects implementing the same interfaces, it is better to implement them as classes using the implements keyword in a class definition, which typically improves memory usage and performance. It also allows for multiple inheritance of interfaces and is another way to populate default behavior for objects.

ff7609af8f
Reply all
Reply to author
Forward
0 new messages