Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to enforce "this interface A should be a descendent of an empty interface B"? #16451

Closed
devdoomari opened this issue Jun 12, 2017 · 7 comments
Labels
Duplicate An existing issue was already created

Comments

@devdoomari
Copy link

I'm trying to enforce "IContext should be a descendent of IContextBase", even though IContextBase is empty.

With current set-up, I can only do:

interface IContextBase {
  __EXTENDS_ICONTEXT_BASE: null;
}
interface IPropsBase {
  __EXTENDS_IPROPS_BASE: null;
}
interface ITestContext extends IContextBase { ... }
class Test1<
   IContext extends IContextBase
   , IProps extends IPropsBase
> { ... }

but this method is quite cumbersome because I have to keep making variables with '__EXTENDS_IPROPS_BASE' in it:

const textContext: ITestContext = {
   __EXTENDS_CONTEXT_BASE: null,
   // ...other fields for ITestContext
}

Is there a better way?

ps: my question on stackoverflow might be relevant: https://stackoverflow.com/questions/44455020/tricking-typescript-compiler-to-assign-wrong-interface-to-a-variable

@ghost
Copy link

ghost commented Jun 12, 2017

Related: #202

@mhegazy mhegazy added the Duplicate An existing issue was already created label Jun 13, 2017
@mhegazy
Copy link
Contributor

mhegazy commented Jun 13, 2017

also see #4895

@devdoomari
Copy link
Author

devdoomari commented Jun 14, 2017

@andy-ms are you refering to #202 (comment) ?

In that case, the following snippet fails :(

@Aleksey-Bykov but that doesn't work for interfaces / etc:

declare class As<S extends string> { 
   private as: S;
}
type Basket = {
   a?: number
} & As<'basket'>

const testBasket: Basket = {} // error: no 'as' in {}

ps: same goes for #202 (comment) (the enum-version)

@devdoomari
Copy link
Author

@mhegazy sorry but I'm having trouble understanding how #4895 is exactly same

@ghost
Copy link

ghost commented Jun 14, 2017

#202 is the issue for nominal types, which seems to be what you want. I don't think there's any solution that would allow you to use an object literal as a nominal type without a cast.
#4895 is related to nominal types since it would allow you to have additional information about an object without actually adding any additional properties to that object.

If you want to enforce that you only accept objects explicitly declared as an IPropsBase, you could always use a function like function createProps<T>(props: T): T & IPropsBase { return props as T & IPropsBase; }, but you would have to call it everywhere you use props.
You could also just use an abstract base class for PropsBase with an unused private member and have everything else inherit from it.

@svieira
Copy link

svieira commented Jun 15, 2017

@devdoomari - as @Aleksey-Bykov pointed out in #202 (comment), but in case you didn't see it, all you need is a cast:

declare class As<S extends string> { 
   private as: S;
}
type Basket = {
   a?: number
} & As<'basket'>

// Casts are always necessary
const testBasket: Basket = {} as Basket

@mhegazy
Copy link
Contributor

mhegazy commented Aug 17, 2017

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@mhegazy mhegazy closed this as completed Aug 17, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants