typescript 基于随机字符串生成随机颜色

基于随机字符串生成随机颜色

random-color-generator.ts
// Assign random color
stringToColour(str: string) {
  var hash = 0;
  for (var i = 0; i < str.length; i++) {
      hash = str.charCodeAt(i) + ((hash << 5) - hash);
  }
  var colour = '#';
  for (var i = 0; i < 3; i++) {
      var value = (hash >> (i * 8)) & 0xFF;
      colour += ('00' + value.toString(16)).substr(-2);
  }
  return colour;
}

console.log( stringToColour('hello') );

typescript 解决方案“错误:gulp-typescript:项目不能同时用于两个编译*。使用createP创建多个项目

解决方案“错误:gulp-typescript:一个项目不能同时用于两个编译*。而是用createProject创建多个项目。”

usage.ts
gulp.task('build', () => {
    let project = tsc.createProject('path/to/tsconfig.json');
    return project.src()
        .pipe(sourcemaps.init())
        .pipe(tsCompileQStream(project)) // wrap the project with the function call
        .js // you don't need this as the wrapper exposes the js stream by default
        .pipe(sourcemaps.write())
        .pipe(gulp.dest(project.options.outDir));
});
gulp-typescript-compile-queue.ts
import {Project, CompileStream} from 'gulp-typescript';
import {Duplex, PassThrough, Readable} from 'stream';
import {Reporter} from 'gulp-typescript/release/reporter';

type Callback = () => void;

/**
 * This is used to ensure that each project object is not busy when it is to be used
 * This prevents the annoying:
 * "Error: gulp-typescript: A project cannot be used in two compilations
 * at the same time. Create multiple projects with createProject instead."
 * @param project The project
 * @param reporter The reporter for the project
 * @returns {CompileStream} compiled project stream
 */
export function tsCompileQStream(project: Project, reporter?: Reporter): CompileStream {

    return new class extends PassThrough implements CompileStream {
        public readonly js: Duplex = this;
        public readonly dts: Duplex = new PassThrough();
        private transformStream: CompileStream;
        private signal: Callback;

        constructor() {
            super({ objectMode: true });
            this.on('pipe', this.checkExistingFlow);
        }

        private checkExistingFlow(src: Readable) {
            this.removeListener('pipe', this.checkExistingFlow);
            src.unpipe(this);

            this.signal = CompileScheduler.scheduleCompilation(project, () => {
                this.transformStream = project(reporter).on('finish', () => this.signal());

                let compileStream = src.pipe(this.transformStream);
                compileStream.js.pipe(this.js);
                compileStream.dts.pipe(this.dts);
            });
        }
    };
}

class CompileScheduler {
    private static compileGateKeeper: Map<Project, Callback[]> = new Map();

    public static scheduleCompilation(project: Project, beginCompilation: Callback): Callback {
        let projectQueue = CompileScheduler.compileGateKeeper.get(project);
        if (!projectQueue) {
            projectQueue = [];
            CompileScheduler.compileGateKeeper.set(project, projectQueue);
        }
        let ret = CompileScheduler.startNext(project);
        if (projectQueue.length) {
            projectQueue.push(beginCompilation);
        } else {
            projectQueue.push(ret);
            beginCompilation();
        }
        return ret;
    }

    private static startNext(project: Project): Callback {
        return () => {
            let projectQueue = CompileScheduler.compileGateKeeper.get(project);
            if (projectQueue.length) {
                let nextCompilation = projectQueue.shift();
                nextCompilation();
            }
        };
    }
    private constructor() {}
}

typescript debounce.ts

debounce.ts
class ReadableStream<T> {
  
  // ...
  
  debounce(milliseconds: number): ReadableStream<T[]>;
  debounce<TOut>(milliseconds: number, join?: (list: T[]) => TOut): ReadableStream<TOut> {
    let lastTime = Date.now();
    let buffer: T[] = [];

    return new ReadableStream<TOut>((push: (value: TOut) => void) => {
      return this.subscribe(value => {
        const now = Date.now();
        buffer.push(value);

        if (now - lastTime < milliseconds) {
          return null;
        }

        const joined = join ? join(buffer) : buffer;

        // This line doesn't compile:
        // Argument of type 'T[] | TOut' is not assignable to parameter of type 'TOut'.
        //   Type 'T[]' is not assignable to type 'TOut'.
        push(joined);
        buffer = [];
        lastTime = now;
      });
    });
  }
}

typescript api.ts

api.ts

interface IChainLink {
  type: 'resource' | 'value';
  value: string;
}

type SingleApiIndex<T> = { [p in keyof T]: SingleApi<T[p]>; };
type ApiIndex<T> = { [p in keyof T]: Api<T[p]>; };

interface BaseApi<T> {
  (id: number): SingleApi<T>;
  _config: T;
  _chain: IChainLink[];
  post(): T;
  delete(): boolean
}

interface SingleApiFunction<T> extends BaseApi<T> {
  get(): T;
}

interface ApiFunction<T> extends BaseApi<T> {
  get(): T[];
}

type SingleApi<T> = SingleApiIndex<T> & SingleApiFunction<T>;
type Api<T> = ApiIndex<T> & ApiFunction<T>;


function parseChain(chain: IChainLink[]): string {
  return '/' + chain.map(link => link.value).join('/');
}

function get<T>(this: Api<T>): T[] {
  const chain = parseChain(this._chain);
  // TODO
}

function post<T>(this: Api<T>): T {
  const chain = parseChain(this._chain);
  // TODO
}

function _delete<T>(this: Api<T>): void {
  const chain = parseChain(this._chain);
  // TODO
}


function makeApi<T>(config: T, chain = [] as IChainLink[]): Api<T> {
  function target(id: number): Api<T> {
    const link: IChainLink = { type: 'value', value: String(id) };
    return makeApi(config, [ link, ...chain ])
  }

  Object.assign(target, {
    _config: config,
    _chain: chain,
    get,
    post,
    delete: _delete,
  });

  return new Proxy({} as Api<T>, {
      get(target, name: string) {
        if (target.hasOwnProperty(name))
          return target[name];

        const link: IChainLink = { type: 'resource', value: name };
        return makeApi(config[name], [ link, ...chain ])
      },
  });
}

class ImageModel {
  toFile() {}
}
class CosasModel {}
class PatataModel {}

const api = makeApi({
  _config: 'patata',
  collections: {
    images: new ImageModel(),
    cosas: new CosasModel(),
    patatas: new PatataModel(),
  },
  users: {
    images: {
      patata: new PatataModel(),
    }
  }
});

const a = api.collections(1)
  .images(3)
  .get()
  .toFile();

function doSomething<T>(api: Api<T>) { return api; }

doSomething(api)
  .collections
  .images
  .post()
  .toFile();

api.collections.images.get()[0].toFile();

typescript 链式回调

链式回调

chain.ts
interface ChainedCallback {
    (): void;
}

class ChainedCallback {
    private chain: Callback[];

    public static fromCallbacks(...cbs: Callback[]): ChainedCallback {
        let chained = new ChainedCallback();
        cbs.forEach(chained.attach);
        return chained;
    }

    public asPromise(): Promise<Error> {
        return new Promise((resolve, reject) => {
            try {
                this();
                resolve(null);
            } catch (e) {
                reject(e);
            }
        });
    }

    public attach(cb: Callback): ChainedCallback {
        this.chain.push(cb);
        return this;
    }

    public exec(): void {
        if (this.chain.length) {
            this.chain.first()();
        }
    }

    public execAll(): void {
        this.chain.forEach(cb => cb());
    }

    public execDetach(): ChainedCallback {
        if (this.chain.length) {
            this.chain.pop()();
        }
        return this;
    }

    public detach(): Callback {
        if (this.chain.length) {
            return this.chain.pop();
        }
        return null;
    }

    public detachAll(): void {
        // https://stackoverflow.com/a/1232046/2089675
        this.chain.splice(0, this.chain.length);
    }

    public execDetachAll(): void {
        // https://stackoverflow.com/a/1232046/2089675
        this.chain.splice(0, this.chain.length)
            .forEach(cb => cb());
    }

    constructor() {
        Object.assign(this.execAll, this);
    }
}

typescript 英雄联盟的打字稿类型在LeagueClient.log中输出lol-champ-select输出

英雄联盟的打字稿类型在LeagueClient.log中输出lol-champ-select输出

championSelectInfo.d.ts
interface ChampionSelectInfo {
    actions: Array<ChampionSelectAction[]> // TODO: are there some times more then one ChampionSelectAction[] items?
    bans: {
        myTeamBans: any[] // TODO: typing
        numBans: number
        theirTeamBans: any[] // TODO: typing
    }
    ceremonials: any[] // TODO: what, why, how? - typing
    chatDetails: {
        /** @example
         * "0n67i+HRx1oOlT+E70pqDtt4qAHMRhu1@sec" */
        chatRoomName: string
        chatRoomPassword: string
    }
    isSpectating: boolean
    /** CellId for the current Player.
     * Can be used to figure out the players actions */
    localPlayerCellId: ChampionSelectCellId
    myTeam: ChampionSelectPlayerInfo[]
    theirTeam: ChampionSelectPlayerInfo[]
    timer: ChampionSelectTimerInfo
    trades: any // TODO: typings
}

interface ChampionSelectAction {
    actorCellId: ChampionSelectCellId,
    championId: number,
    completed: boolean,
    /** action Id (continuos?) */
    id: number,
    pickTurn: number,
    type: "pick" | "ban"
}

type ChampionSelectCellId = number

interface ChampionSelectPlayerInfo {
    assignedPosition: ChampionSelectPosition,
    cellId: ChampionSelectCellId,
    /** 0 for Bots (or when not visible?) */
    championId: 84,
    /** 0 if nothing picked */
    championPickIntent: number,
    displayName: string,
    playerType: "PLAYER" | "BOT",
    /** built by <championId><skin id padded to 3 digits>
     * @example
     * 84007 = championId: 84, skindId: 7*/
    selectedSkinId: number,
    spell1Id: ChampionSelectSpellId,
    spell2Id: ChampionSelectSpellId,
    /** equivalent to 100/200 TODO: make sure this is correct */
    team: 1 | 2,
    /** -1 for bots (and if not visible?) 0 = default ? */
    wardSkinId: number // TODO: make sure doc is correct
}

/** for bots this number can either be 0 or something completely bogus like 18446744073709551615 */
type ChampionSelectSpellId = number

type ChampionSelectPosition = string // TODO: typing as union type of possibilities

interface ChampionSelectTimerInfo {
    adjustedTimeLeftInPhase: number,
    adjustedTimeLeftInPhaseInSec: number,
    internalNowInEpochMs: number,
    isInfinite: boolean,
    phase: ChampionSelectPhase,
    timeLeftInPhase: number,
    timeLeftInPhaseInSec: number,
    totalTimeInPhase: number
}

typescript 如何使用服务,请参阅评论。

如何使用服务,请参阅评论。

new_gist_file.ts
// We have to define the constructor that angular is going to use when instanciate the class
// We have to define constructor, providers, import the class and use it

import { LoggingService } from './../logging.service'; // Import the service
import { Component, EventEmitter, Output } from '@angular/core';

@Component({
  selector: 'app-new-account',
  templateUrl: './new-account.component.html',
  styleUrls: ['./new-account.component.css'],
  providers: [LoggingService] // We have to inform angular about our dependency
})
export class NewAccountComponent {
  @Output() accountAdded = new EventEmitter<{name: string, status: string}>();

  // The type of the parameter must be the class of the service
  constructor(private loggingService: LoggingService) {}

  onCreateAccount(accountName: string, accountStatus: string) {
    this.accountAdded.emit({
      name: accountName,
      status: accountStatus
    });
    this.loggingService.logStatusChange(accountStatus); // Use the method of the service
  }
}

typescript 创建数据模型并使用* ngFor输出数据

创建数据模型并使用* ngFor输出数据

recipe.model.ts
// define how a single Recipe will look like (a blueprint for objects we create)
export class Recipe {
  public name: string;
  public description: string;
  public imagePath: string;

  // a constructor is a built in function
  constructor(name: string, desc: string, imagePath: string) {
    this.name = name;
    this.description = desc;
    this.imagePath = imagePath;
  }
}
recipe-list.component.ts
<div class="row">
  <div class="col-xs-12">
    <div class="btn btn-success">New Recipe</div>
    <br><br>
  </div>
</div>
<div class="row">
  <div class="col-xs-12">
    <a href="" class="list-group-item clearfix" *ngFor="let recipe of recipes">
      <span class="pull-left">
        <h4 class="list-group-item-heading">{{ recipe.name }}</h4>
        <p class="list-group-item-text">{{ recipe.description }}</p>
      </span>
      <span class="pull-right">
        <!-- src="{{ recipe.imagePath }}" will also work -->
        <img class="img-responsive" [src]="recipe.imagePath" alt="{{ recipe.name }}" style="max-height: 50px;">
      </span>
    </a>
    <app-recipe-item></app-recipe-item>
  </div>
</div>

typescript 自定义事件中发生事件时无传递数据。 <br/> <br/>资料来源:Angular course udemy,第6节,第79讲

自定义事件中发生事件时无传递数据。 <br/> <br/>资料来源:Angular course udemy,第6节,第79讲

no_pass_data_in_custom_event.ts
@output propertyName = new EventEmmiter<void>();

typescript textarea的扩展,full.component.ts

textarea-expanded-full.component.ts
import { Component, forwardRef, Renderer2, ViewChild } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

export const EPANDED_TEXTAREA_VALUE_ACCESSOR : any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => TextareaExpandedComponent),
  multi: true,
};

@Component({
  selector: 'textarea-expanded',
  providers: [EPANDED_TEXTAREA_VALUE_ACCESSOR],
  template: `
    <div contenteditable="true"
         #textarea
         tabindex="1"
         (input)="change($event)"
         role="textarea"></div>
  `,
  styles: [`
    div {
      width: 200px;
      min-height: 50px;
      border: 1px solid lightgray;
    }

    div.disabled {
      cursor: not-allowed;
      opacity: 0.5;
      pointer-events: none;
    }
  `]
})
export class TextareaExpandedComponent implements ControlValueAccessor {
  @ViewChild('textarea') textarea;

  onChange;
  onTouched;

  writeValue( value : any ) : void {
    const div = this.textarea.nativeElement;
    this.renderer.setProperty(div, 'textContent', value);
  }

  registerOnChange( fn : any ) : void {
    this.onChange = fn;
  }

  registerOnTouched( fn : any ) : void {
    this.onTouched = fn;
  }

  setDisabledState( isDisabled : boolean ) : void {
    const div = this.textarea.nativeElement;
    const action = isDisabled ? 'addClass' : 'removeClass';
    this.renderer[action](div, 'disabled');
  }

  constructor( private renderer : Renderer2 ) {
  }

  change( $event ) {
    this.onChange($event.target.textContent);
    this.onTouched($event.target.textContent);
  }

}

// <textarea-expanded [formControl]="control"></textarea-expanded>