The TypeScript code defines a PropsProtector class to manage and validate property overrides through BaseProps and UserProps derived from an IProps interface. The AbstractClass sets immutable base properties, while ConcreteClass sets user properties. It then creates an instance of ConcreteClass, which logs a merged object of base and user properties, ensuring no base properties are overridden.

interface IProps {
  foo: string;
  bar: number;
  baz: boolean;

type BaseProps = Pick<IProps, "foo">;
type UserProps = Omit<IProps, keyof BaseProps>;

abstract class PropsProtector {
  protected abstract baseProps: any;
  public abstract props: any;

  protected getProps(): IProps {
    return {

  private validateUserProps(): void {
    const baseKeys = Object.keys(this.baseProps);
    const userKeys = Object.keys(this.props);

    baseKeys.forEach(key => {
      if (userKeys.includes(key)) {
        throw new Error(`You can not override ${key}!`);

abstract class AbstractClass extends PropsProtector {
  protected baseProps: BaseProps = {
    foo: "first"
  public abstract props: UserProps;

class ConcreteClass extends AbstractClass {
  constructor() {

  public props = {
    bar: 2,
    baz: true

const concrete = new ConcreteClass();