These aren't as good as [[20210816112700-private-class-syntax-js]], but can fill a niche. In particular, making a semi-private prop in an interface for a [[react]] component.
This is a common want in design systems, as they often have internal exceptions that don't exist for consumers. Usually, a prop that is dangerous and needs to be handled with care.
type Props = { tooltip: string; hideTooltip?: boolean; // ideally this is private } function ButtonWithTooltip(props: Props) {}
There's not great way to make the hideTooltip
prop private. We can extend the type and make an InternalProps
, but then the component needs to also use it, making it effectively pointless.
type Props = { tooltip: string; } type InternalProps = Props & { hideTooltip?: boolean; // ideally this is private } function ButtonWithTooltip(props: InternalProps) {} // <ButtonWithTooltip tooltip="Hello" hideTooltip /> is valid
The key factor here is using Symbols: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol Symbols are guaranteed to be unique. The same string does not create an equal symbol:
const symbolA = Symbol('uniqueKey'); const symbolB = Symbol('uniqueKey'); console.log(symbolA === symbolB); // false
Using this, you can add a property to a type or interface that corresponds to a symbol. That instance of a symbol is the only thing that is equivalent to the prop.
const INTERNAL_HIDE_TOOLTIP = Symbol('hideTooltip'); // Not exported, but used internally across files
const EXTERNAL_HIDE_TOOLTIP = Symbol('hideTooltip'); // A user trying to re-create the symbol
const TEST = Symbol('internalFlag')
type Props = {
tooltip: string
}
// Still use internal copy to keep the exports 'clean'
type InternalProps = Props & {
[INTERNAL_HIDE_TOOLTIP]?: boolean
}
// This would be JSX, but written in TS to test execution
function Component(props: InternalProps) { console.log(props.tooltip, !!props[INTERNAL_HIDE_TOOLTIP]) }
Component({ tooltip: 'Standard' })
Component({ tooltip: 'Internal usage', [INTERNAL_HIDE_TOOLTIP]: true })
Component({ tooltip: 'Does not work', INTERNAL_HIDE_TOOLTIP: true })
Component({ tooltip: 'Does not work', [EXTERNAL_HIDE_TOOLTIP]: true })
The same can be done for regular JS Objects, not just Typescript types.
This isn't truly private though, so use it with caution. You can get this through things like Object.getOwnPropertySymbols
. However it's definitely an improvement and is decently abstracted away.
Note in React, there's a chance that this is stripped if React tries to 'normalize' your props, but I cannot confirm yet.