-
Notifications
You must be signed in to change notification settings - Fork 539
Change Recipes
This is a collection of some patterns that can be used to manage changes that will eventually be or would otherwise be breaking.
Deprecating an API in order to change it to be @internal
may be handled without the internal usages appearing deprecated (requiring no-deprecated
lint disables). Since standard API separation is generated from a single file, a split is required with a re-tagged API to make this work.
- Apply the
@deprecated
tag to the original API. It is important to keep the original tags in place to make it clear that API is externally exposed. - Create a new
internal.ts
source next toindex.ts
that re-exports everything:export * from "./index.js";
. - Add a new named export copying the API. Import original renamed and exported as copy.
- Change package.json export for
/internal
tointernal.*
instead ofindex.*
. - As needed, apply policy required changes. Try
pnpm policy-check:fix
.
Example: PR 23332: Making ContainerRuntime externally deprecated - see files under packages/runtime/container-runtime
Classes and enums are both values and types and the type of (typeof
) the value is not the same as the type. To clone a class or enum fully, both a type and value should be cloned.
packages/runtime/container-runtime/src/internal.ts of PR 23332 avoids @deprecated
for /internal
version of ContainerRuntime
.
import { ContainerRuntime as ContainerRuntimeClass } from "./containerRuntime.js";
export type ContainerRuntime = ContainerRuntimeClass;
export const ContainerRuntime = ContainerRuntimeClass;
There is no known simple way to clone a namespace. To clone a namespace it needs redeclared member by member. So it may be advantageous to only resurface the minimal members when needed. (api-extractor
may insist in a large "internal" namespace be exposed, but for /internal
uses only tiny number of set actually needs surfaced.)
Example: TODO - use jason-ha's pending core-interfaces reorg for Presence infrastructure
Classes exposed outside of a package often lead to undesired maintenance burdens complicating change and evolution. When a class does not have protected members including transitive ones from extends
specification, then an essentially type equivalent interface and new function may be substituted.
Warning The replacement
interface
will not provide exact type checking protections that the originalclass
afforded. If the class was not already@sealed
, then understand if any customer may have had reason to inherit the class.
- Add replacement exported interface using original class name.
-
extends
the interface by allimplements
specifications. - Copy declaration of all class public members not covered by
extends
(above) into the interface. - Be sure the interface is
@sealed
even if original class was not.
-
- Rename the class and extend from the interface.
- Add an exported
const
variable using original class name.- Type as a union of
-
new
function using class constructor's parameters and returning the interface - object declaration containing public static class members
-
- Assign to it the renamed class.
- Type as a union of
- If class had any static members that had original class types and accessed private members, there will need to be a cast (
as
) to renamed class.
Before
export interface A {
value: number;
}
export class B implements A {
public readonly id: string = "instanceOfB";
private readonly shh = 98;
public constructor(public value: number) {}
public static readPrivate(bThis: B): number {
return bThis.shh;
}
}
After
export interface A {
value: number;
}
// Step 1
/** @sealed */
export interface B extends A {
readonly id: string;
}
// Step 2
class BImpl implements B {
public readonly id = "instanceOfB";
private readonly shh = 98;
public constructor(public value: number) {}
public static readPrivate(bThis: B): number {
return (bThis as BImpl).shh; // Step 4
}
}
// Step 3
export const B: (new (value: number) => B) & {
readPrivate: (bThis: B) => number;
} = BImpl;
This wiki is focused on contributing to the Fluid Framework codebase.
For information on using Fluid Framework or building applications on it, please refer to fluidframework.com.
- Submitting Bugs and Feature Requests
-
Contributing to the Repo
- Repo Basics
- Common Workflows and Patterns
- Managing dependencies
- Client Code
- Server Code
- PR Guidelines
- CI Pipelines
- Breaking vs Non-Breaking Changes
- Branches, Versions, and Releases
- Compatibility & Versioning
- Testing
- Debugging
- npm package scopes
- Maintaining API support levels
- Developer Tooling Maintenance
- API Deprecation
- Working with the Website (fluidframework.com)
- Coding Guidelines
- Documentation Guidelines
- CLA