Home:ALL Converter>Interface type check with Typescript

Interface type check with Typescript

Ask Time:2013-01-20T22:37:38         Author:lhk

Json Formatter

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 ?

Author:lhk,eproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/14425568/interface-type-check-with-typescript
Fenton :

You can achieve what you want without the instanceof keyword as you can write custom type guards now:\ninterface A {\n member: string;\n}\n\nfunction instanceOfA(object: any): object is A {\n return 'member' in object;\n}\n\nvar a: any = {member: "foobar"};\n\nif (instanceOfA(a)) {\n alert(a.member);\n}\n\nLots of Members\nIf 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.\ninterface A {\n discriminator: 'I-AM-A';\n member: string;\n}\n\nfunction instanceOfA(object: any): object is A {\n return object.discriminator === 'I-AM-A';\n}\n\nvar a: any = {discriminator: 'I-AM-A', member: "foobar"};\n\nif (instanceOfA(a)) {\n alert(a.member);\n}\n",
2013-01-20T15:57:07
Dmitry Matveev :

TypeGuards\n\ninterface MyInterfaced {\n x: number\n}\n\nfunction isMyInterfaced(arg: any): arg is MyInterfaced {\n return arg.x !== undefined;\n}\n\nif (isMyInterfaced(obj)) {\n (obj as MyInterfaced ).x;\n}\n",
2017-06-28T09:54:57
Evan Crooks :

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:\n@Input() data?: any;\n@Input() discriminator?: string;\n\nngOnInit(){\n if(this.discriminator = 'InterfaceAName'){\n //do stuff\n }\n else if(this.discriminator = 'InterfaceBName'){\n //do stuff\n }\n}\n\nObviously 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:\n<div [(ngModel)]={{dataModel}}>\n <div *ngFor="let attr of (data | keyvalue)">\n <!--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 -->\n </div>\n</div>\n\nOr This Instead:\n<div *ngIf = "model == 'InterfaceAName'">\n <div>Do This Stuff</div>\n</div>\n<div *ngIf= "model == 'IntefaceBName'">\n <div>Do this instead</div>\n</div>\n\n(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)\nI 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.",
2022-06-02T14:22:20
vilicvane :

In TypeScript 1.6, user-defined type guard will do the job.\n\ninterface Foo {\n fooProperty: string;\n}\n\ninterface Bar {\n barProperty: string;\n}\n\nfunction isFoo(object: any): object is Foo {\n return 'fooProperty' in object;\n}\n\nlet object: Foo | Bar;\n\nif (isFoo(object)) {\n // `object` has type `Foo`.\n object.fooProperty;\n} else {\n // `object` has type `Bar`.\n object.barProperty;\n}\n\n\nAnd just as Joe Yang mentioned: since TypeScript 2.0, you can even take the advantage of tagged union type.\n\ninterface Foo {\n type: 'foo';\n fooProperty: string;\n}\n\ninterface Bar {\n type: 'bar';\n barProperty: number;\n}\n\nlet object: Foo | Bar;\n\n// You will see errors if `strictNullChecks` is enabled.\nif (object.type === 'foo') {\n // object has type `Foo`.\n object.fooProperty;\n} else {\n // object has type `Bar`.\n object.barProperty;\n}\n\n\nAnd it works with switch too.",
2015-11-16T10:35:45
Anthony Gingrich :

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.\nMy 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:\nClass Decorator:\nRecursively 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).\nPros: 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.\nCons: 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.\nCloning:\nVery 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.\nPros: 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.\nCons: 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.\nAll 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.",
2021-10-31T15:04:48
troYman :

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.\nI provide you mine solution which works if there is at least one method in the interface\nexport class SomeObject implements interfaceA {\n public methodFromA() {}\n}\n\nexport interface interfaceA {\n methodFromA();\n}\n\nCheck if object is of type interface:\nconst obj = new SomeObject();\nconst objAsAny = obj as any;\nconst objAsInterfaceA = objAsAny as interfaceA;\nconst isObjOfTypeInterfaceA = objAsInterfaceA.methodFromA != null;\nconsole.log(isObjOfTypeInterfaceA)\n\nNote: We will get true even if we remove 'implements interfaceA' because the method still exists in the SomeObject class",
2020-07-22T16:55:11
Caleb Macdonald Black :

How about User-Defined Type Guards? https://www.typescriptlang.org/docs/handbook/advanced-types.html\n\ninterface Bird {\n fly();\n layEggs();\n}\n\ninterface Fish {\n swim();\n layEggs();\n}\n\nfunction isFish(pet: Fish | Bird): pet is Fish { //magic happens here\n return (<Fish>pet).swim !== undefined;\n}\n\n// Both calls to 'swim' and 'fly' are now okay.\n\nif (isFish(pet)) {\n pet.swim();\n}\nelse {\n pet.fly();\n}\n",
2016-07-16T00:37:48
aledpardo :

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.\n\nDepending 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.\n\nfunction implementsTKeys<T>(obj: any, keys: (keyof T)[]): obj is T {\n if (!obj || !Array.isArray(keys)) {\n return false;\n }\n\n const implementKeys = keys.reduce((impl, key) => impl && key in obj, true);\n\n return implementKeys;\n}\n\n\nExample of usage:\n\ninterface A {\n propOfA: string;\n methodOfA: Function;\n}\n\nlet objectA: any = { propOfA: '' };\n\n// Check if objectA partially implements A\nlet implementsA = implementsTKeys<A>(objectA, ['propOfA']);\n\nconsole.log(implementsA); // true\n\nobjectA.methodOfA = () => true;\n\n// Check if objectA fully implements A\nimplementsA = implementsTKeys<A>(objectA, ['propOfA', 'methodOfA']);\n\nconsole.log(implementsA); // true\n\nobjectA = {};\n\n// Check again if objectA fully implements A\nimplementsA = implementsTKeys<A>(objectA, ['propOfA', 'methodOfA']);\n\nconsole.log(implementsA); // false, as objectA now is an empty object\n",
2019-08-26T14:13:06
Jack Miller :

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.\ninterface A{\n member:string;\n}\n\nconst implementsA = (o: object): boolean => {\n try {\n return 'member' in o;\n } catch (error) {\n return false;\n }\n}\n\nconst a:any={member:"foobar"};\n\nimplementsA(a) && console.log("a implements A");\n// implementsA("str"); // causes TS transpiler error\n",
2022-02-24T07:20:09
Joe Yang :

typescript 2.0 introduce tagged union\n\nTypescript 2.0 features\n\ninterface Square {\n kind: \"square\";\n size: number;\n}\n\ninterface Rectangle {\n kind: \"rectangle\";\n width: number;\n height: number;\n}\n\ninterface Circle {\n kind: \"circle\";\n radius: number;\n}\n\ntype Shape = Square | Rectangle | Circle;\n\nfunction area(s: Shape) {\n // In the following switch statement, the type of s is narrowed in each case clause\n // according to the value of the discriminant property, thus allowing the other properties\n // of that variant to be accessed without a type assertion.\n switch (s.kind) {\n case \"square\": return s.size * s.size;\n case \"rectangle\": return s.width * s.height;\n case \"circle\": return Math.PI * s.radius * s.radius;\n }\n}\n",
2016-07-13T09:51:44
xinthose :

I found an example from @progress/kendo-data-query in file filter-descriptor.interface.d.ts\nChecker\ndeclare const isCompositeFilterDescriptor: (source: FilterDescriptor | CompositeFilterDescriptor) => source is CompositeFilterDescriptor;\n\nExample usage\nconst filters: Array<FilterDescriptor | CompositeFilterDescriptor> = filter.filters;\n\nfilters.forEach((element: FilterDescriptor | CompositeFilterDescriptor) => {\n if (isCompositeFilterDescriptor(element)) {\n // element type is CompositeFilterDescriptor\n } else {\n // element type is FilterDescriptor\n }\n});\n",
2020-08-21T15:58:53
Faliorn :

I know the question is a bit old, but just my 50 cents. This worked for me:\nconst container: Container = icc.controlComponent as unknown as Container;\nif (container.getControlComponents) {\n this.allControlComponents.push(...container.getControlComponents());\n}\n\nContainer is the interface, and icc.controlComponent is the object I wanted to check, and getControlComponents is a method from Container interface.",
2022-11-09T09:21:22
pcan :

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\n\nUsage example:\n\nIn one of your typescript files, create an interface and a class that implements it like the following:\n\ninterface MyInterface {\n doSomething(what: string): number;\n}\n\nclass MyClass implements MyInterface {\n counter = 0;\n\n doSomething(what: string): number {\n console.log('Doing ' + what);\n return this.counter++;\n }\n}\n\n\nnow let's print some the list of implemented interfaces.\n\nfor (let classInterface of MyClass.getClass().implements) {\n console.log('Implemented interface: ' + classInterface.name)\n}\n\n\ncompile with reflec-ts and launch it:\n\n$ node main.js\nImplemented interface: MyInterface\nMember name: counter - member kind: number\nMember name: doSomething - member kind: function\n\n\nSee reflection.d.ts for Interface meta-type details.\n\nUPDATE:\nYou can find a full working example here",
2015-11-12T11:50:31
edbentley :

You can validate a TypeScript type at runtime using ts-validate-type, like so (does require a Babel plugin though):\n\nconst user = validateType<{ name: string }>(data);\n",
2020-04-14T13:51:13
Ledom :

Here's the solution I came up with using classes and lodash: (it works!)\n// TypeChecks.ts\nimport _ from 'lodash';\n\nexport class BakedChecker {\n private map: Map<string, string>;\n\n public constructor(keys: string[], types: string[]) {\n this.map = new Map<string, string>(keys.map((k, i) => {\n return [k, types[i]];\n }));\n if (this.map.has('__optional'))\n this.map.delete('__optional');\n }\n\n getBakedKeys() : string[] {\n return Array.from(this.map.keys());\n }\n\n getBakedType(key: string) : string {\n return this.map.has(key) ? this.map.get(key) : "notfound";\n }\n}\n\nexport interface ICheckerTemplate {\n __optional?: any;\n [propName: string]: any;\n}\n\nexport function bakeChecker(template : ICheckerTemplate) : BakedChecker {\n let keys = _.keysIn(template);\n if ('__optional' in template) {\n keys = keys.concat(_.keysIn(template.__optional).map(k => '?' + k));\n }\n return new BakedChecker(keys, keys.map(k => {\n const path = k.startsWith('?') ? '__optional.' + k.substr(1) : k;\n const val = _.get(template, path);\n if (typeof val === 'object') return val;\n return typeof val;\n }));\n}\n\nexport default function checkType<T>(obj: any, template: BakedChecker) : obj is T {\n const o_keys = _.keysIn(obj);\n const t_keys = _.difference(template.getBakedKeys(), ['__optional']);\n return t_keys.every(tk => {\n if (tk.startsWith('?')) {\n const ak = tk.substr(1);\n if (o_keys.includes(ak)) {\n const tt = template.getBakedType(tk);\n if (typeof tt === 'string')\n return typeof _.get(obj, ak) === tt;\n else {\n return checkType<any>(_.get(obj, ak), tt);\n }\n }\n return true;\n }\n else {\n if (o_keys.includes(tk)) {\n const tt = template.getBakedType(tk);\n if (typeof tt === 'string')\n return typeof _.get(obj, tk) === tt;\n else {\n return checkType<any>(_.get(obj, tk), tt);\n }\n }\n return false;\n }\n });\n}\n\ncustom classes:\n// MyClasses.ts\n\nimport checkType, { bakeChecker } from './TypeChecks';\n\nclass Foo {\n a?: string;\n b: boolean;\n c: number;\n\n public static _checker = bakeChecker({\n __optional: {\n a: ""\n },\n b: false,\n c: 0\n });\n}\n\nclass Bar {\n my_string?: string;\n another_string: string;\n foo?: Foo;\n\n public static _checker = bakeChecker({\n __optional: {\n my_string: "",\n foo: Foo._checker\n },\n another_string: ""\n });\n}\n\nto check the type at runtime:\nif (checkType<Bar>(foreign_object, Bar._checker)) { ... }\n",
2020-08-17T15:36:11
Willem van der Veen :

Type guards in Typescript:\nTS has type guards for this purpose. They define it in the following manner:\n\nSome expression that performs a runtime check that guarantees the type\nin some scope.\n\nThis basically means that the TS compiler can narrow down the type to a more specific type when it has sufficient information. For example:\nfunction foo (arg: number | string) {\n if (typeof arg === 'number') {\n // fine, type number has toFixed method\n arg.toFixed()\n } else {\n // Property 'toFixed' does not exist on type 'string'. Did you mean 'fixed'?\n arg.toFixed()\n // TSC can infer that the type is string because \n // the possibility of type number is eliminated at the if statement\n }\n}\n\nTo 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:\ninterface Dog {\n bark: () => void;\n}\n\n// The function isDog is a user defined type guard\n// the return type: 'pet is Dog' is a type predicate, \n// it determines whether the object is a Dog\nfunction isDog(pet: object): pet is Dog {\n return (pet as Dog).bark !== undefined;\n}\n\nconst dog: any = {bark: () => {console.log('woof')}};\n\nif (isDog(dog)) {\n // TS now knows that objects within this if statement are always type Dog\n // This is because the type guard isDog narrowed down the type to Dog\n dog.bark();\n}\n",
2020-10-31T09:56:09
Björn Hjorth :

Type guards in Typescript using Reflect\nHere is an example of a type guard from my Typescript game engine\n export interface Start {\n /**\n * Start is called on the frame when a script is enabled just before any of the Update methods are called the first time.\n */\n start(): void\n }\n\n\n/**\n * User Defined Type Guard for Start\n */\n export const implementsStart = (arg: any): arg is Start => {\n return Reflect.has(arg, 'start')\n } \n\n\n /**\n * Example usage of the type guard\n */\n\n start() {\n this.components.forEach(component => {\n if (implementsStart(component)) {\n component.start()\n } \n\n })\n}\n",
2021-09-05T16:45:17
DS. :

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.\n\nFor OP's example,\n\n\ninterface A {\n member: string;\n}\n\n\nYou'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:\n\nimport fooDesc from './foo-ti.ts';\nimport {createCheckers} from \"ts-interface-checker\";\nconst {A} = createCheckers(fooDesc);\n\nA.check({member: \"hello\"}); // OK\nA.check({member: 17}); // Fails with \".member is not a string\" \n\n\nYou can create a one-liner type-guard function:\n\nfunction isA(value: any): value is A { return A.test(value); }\n",
2018-01-26T05:26:07
Geoff Davids :

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.\nYou can check if something is a string or number and use it as such, without the compiler complaining:\nimport { is } from 'typescript-is';\n\nconst wildString: any = 'a string, but nobody knows at compile time, because it is cast to `any`';\n\nif (is<string>(wildString)) { // returns true\n // wildString can be used as string!\n} else {\n // never gets to this branch\n}\n\nif (is<number>(wildString)) { // returns false\n // never gets to this branch\n} else {\n // Now you know that wildString is not a number!\n}\n\nYou can also check your own interfaces:\nimport { is } from 'typescript-is';\n\ninterface MyInterface {\n someObject: string;\n without: string;\n}\n\nconst foreignObject: any = { someObject: 'obtained from the wild', without: 'type safety' };\n\nif (is<MyInterface>(foreignObject)) { // returns true\n const someObject = foreignObject.someObject; // type: string\n const without = foreignObject.without; // type: string\n}\n",
2021-11-18T01:43:31
Daniel Ribeiro :

I would like to point out that TypeScript does not provide a direct mechanism for dynamically testing whether an object implements a particular interface. \n\nInstead, TypeScript code can use the JavaScript technique of checking whether an appropriate set of members are present on the object. For example:\n\nvar obj : any = new Foo();\n\nif (obj.someInterfaceMethod) {\n ...\n}\n",
2015-03-11T19:18:38
Botond :

export interface ConfSteps {\n group: string;\n key: string;\n steps: string[];\n}\n\n\nprivate verify(): void {\n const obj = `{\n \"group\": \"group\",\n \"key\": \"key\",\n \"steps\": [],\n \"stepsPlus\": []\n } `;\n if (this.implementsObject<ConfSteps>(obj, ['group', 'key', 'steps'])) {\n console.log(`Implements ConfSteps: ${obj}`);\n }\n }\n\n\nprivate objProperties: Array<string> = [];\n\nprivate implementsObject<T>(obj: any, keys: (keyof T)[]): boolean {\n JSON.parse(JSON.stringify(obj), (key, value) => {\n this.objProperties.push(key);\n });\n for (const key of keys) {\n if (!this.objProperties.includes(key.toString())) {\n return false;\n }\n }\n this.objProperties = null;\n return true;\n }\n",
2019-10-18T07:21:46
Dan Dohotaru :

same as above where user-defined guards were used but this time with an arrow function predicate\n\ninterface A {\n member:string;\n}\n\nconst check = (p: any): p is A => p.hasOwnProperty('member');\n\nvar foo: any = { member: \"foobar\" };\nif (check(foo))\n alert(foo.member);\n",
2018-02-21T12:07:31
Arnold Vakaria :

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.\ndeclare var HTMLIFrameElement: {\n prototype: HTMLIFrameElement;\n new(): HTMLIFrameElement;\n};\n\nSo in this situation\ninterface A {\n member:string;\n}\n\ndeclare var A : {\n prototype: A;\n new(): A;\n};\n\nif(a instanceof A) alert(a.member);\n\nshould work fine",
2022-04-01T10:52:18
frodeborli :

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.\nThis is the purpose for which ES6 has symbols.\nInterface\n// Notice there is no naming conflict, because interfaces are a *type*\nexport const IAnimal = Symbol("IAnimal"); \nexport interface IAnimal {\n [IAnimal]: boolean; // the fubber\n}\n\nexport const IDog = Symbol("IDog");\nexport interface IDog extends IAnimal {\n [IDog]: boolean;\n}\n\nexport const IHound = Symbol("IDog");\nexport interface IHound extends IDog {\n // The fubber can also be typed as only 'true'; meaning it can't be disabled.\n [IDog]: true;\n [IHound]: boolean;\n}\n\nClass\nimport { IDog, IAnimal } from './interfaces';\nclass Dog implements IDog {\n // Multiple fubbers to handle inheritance:\n [IAnimal] = true;\n [IDog] = true;\n}\n\nclass Hound extends Dog implements IHound {\n [IHound] = true;\n}\n\nTesting\nThis code can be put in a type guard if you want to help the TypeScript compiler.\nimport { IDog, IAnimal } from './interfaces';\n\nlet dog = new Dog();\n\nif (dog instanceof Hound || dog[IHound]) {\n // false\n}\nif (dog[IAnimal]?) {\n // true\n}\n\nlet houndDog = new Hound();\n\nif (houndDog[IDog]) {\n // true\n}\n\nif (dog[IDog]?) {\n // it definitely is a dog\n}\n\n",
2021-04-28T13:41:16
John Miller :

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.\nLet's say I have a very simple function that needs to know a parameter's interface type:\nconst simpleFunction = (canBeTwoInterfaces: interfaceA | interface B) => { \n // if interfaceA, then return canBeTwoInterfaces.A\n // if interfaceB, then return canBeTwoInterfaces.B\n}\n\nThe answers that are getting the most upvotes tend to be using "function checking". i.e.,\nconst simpleFunction = (canBeTwoInterfaces: interfaceA | interface B) => { \n if (canBeTwoInterfaces.onlyExistsOnInterfaceA) return canBeTwoInterfaces.A\n else return canBeTwoInterfaces.B\n}\n\nHowever, 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.\nLike 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.\nThis is what I would do:\nconst simpleFunction = (\n canBeTwoInterfaces: interfaceA | interface B,\n whichInterfaceIsIt: 'interfaceA' | 'interfaceB'\n) => { \n if (whichInterfaceIsIt === 'interfaceA') return canBeTwoInterface.A\n else return canBeTwoInterfaces.B\n}\n",
2021-08-29T18:52:39
yy