Moxhe
Front-End Coder
function greeter(person: string) {
return "Hello, " + person;
}
let user = [0, 1, 2];
// error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.
document.body.innerHTML = greeter(user);
许多长时间的typescript开发者
对typescript的理解和使用水平还停留在入门阶段
开发仅限于基本的类型标注且any泛滥
// assets/utils/report.ts
export function converStringFromObj(obj: any) { }
// assets/utils/bom.ts
export function reportConfig(config: any, init: boolean = false) { }
// assets/utils/copy.ts
let copyInput: any;
// components/AppGuide/types.ts
// tslint:disable:variable-name
export const StatusMap: any = {
all: '下载', // 不能判断
down: '下载', // 未安装
open: '打开', // 已安装
install: '安装',
ing: '下载中',
failed: '下载失败',
};
天天吐槽基础库没有文档
又不知道写declaration file(.d.ts)
// assets/types/global.d.ts
// 及其重要的库
declare module '@tencent/impush-client' {
const impushClient: any;
export default impushClient;
}
declare module '@tencent/edu-report' {
const eduReport: any;
export default eduReport;
}
declare module '@tencent/im-user' {
const User: any;
export default User;
}
许多来自Definitely Types的报错却不明白为什么
// class Container
// 类型“typeof Container”的参数不能赋给类型“ComponentType<Matching<{ courseInfo: CourseInfoState; catalogInfo: CatalogInfoState; discountInfo...”的参数。
// 不能将类型“typeof Container”分配给类型“FunctionComponent<Matching<{ courseInfo: CourseInfoState; catalogInfo: CatalogInfoState; discount...”。
// 类型“typeof Container”提供的内容与签名“(props: PropsWithChildren<Matching<{ courseInfo: CourseInfoState; catalogInfo: CatalogInfoState; discountInfo: DiscountInfoState; playInfo: PlayInfoState; userInfo: UserInfoState; controlInfo: ControlInfoState; topicList: Topic[]; } & typeof import("d:/projects/m-core/src/pages/course/actions/index"), ContainerProps>>, context?: any): ReactElement<any, string | ((props: any) => ReactElement<any, string | any | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)> | null”不匹配。ts(2345)
export default connect(
(state: AppState) => ({ ...state }),
dispatch =>
bindActionCreators(
actions,
dispatch,
),
)(Container);
没有细究tsconfig.json配置
{
"compilerOptions": {
"baseUrl": ".",
// ...
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"noImplicitAny": true,
"lib": ["es6", "es2017", "dom", "dom.iterable", "scripthost"],
"target": "es5",
"module": "ESNext",
"jsx": "react",
"noUnusedLocals": true,
"typeRoots": ["node_modules/@types", "src/assets/types"],
"sourceMap": true,
"importHelpers": true
},
"include": ["src/**/*"],
"exclude": ["src/edu_modules/**/*", ".template/**/*"]
}
复杂场景不知道如何实现正确的类型校验
export default function debounce(fn: Function, delay: number, immediate?: boolean) {
let timer: number = null;
return function (...args: any[]) {
const context = this;
clearTimeout(timer);
const callNow = !timer;
timer = window.setTimeout(() => {
fn.apply(context, args);
timer = null;
}, delay);
if (immediate && callNow) {
fn.apply(context, args);
}
};
}
// Fixed Length Tuples
interface NumStrTuple
extends Array<number | string> {
0: number;
1: string;
// using the numeric literal type '2'
length: 2;
}
// Weixin Uin
type CGIData = {
uin: number | bigint
};
// object is non-primitive type
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK
create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error
// type assertions
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
// Readonly properties
interface Point {
readonly x: number;
readonly y: number;
}
// Optional Properties
interface SquareConfig {
color?: string;
width?: number;
}
interface SquareConfig {
color?: string;
width?: number;
// Indexable Properties
// [propName: string]: any;
}
function createSquare(config: SquareConfig): { color: string; area: number } {}
// Error, fails silently in Javascript
createSquare({ colour: "red", width: 100 });
// Function Types
interface SearchFunc {
(source: string, subString: string): boolean;
}
class Square implements SquareConfig {
color: string;
width: number;
}
class Employee {
static sum = 1;
readonly state: string;
private _fullName: string;
protected age: number;
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
this._fullName = newName;
}
}
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log("roaming the earth...");
}
}
// typing function
function add(x: number, y: number): number {
return x + y;
}
// optional parameters
function buildName(
firstName: string = 'Bob',
lastName?: string,
...restOfName: string[],
) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
function f(this: void) {
// make sure `this` is unusable in this standalone function
}
// Overloads
function pickCard(x: {suit: string; card: number; }[]): number;
function pickCard(x: number): { suit: string; card: number; };
function pickCard(x): any {
if (typeof x == "object") {
}
else if (typeof x == "number") {
}
}
// generic function
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("myString")
interface GenericIdentityFn<T> {
(arg: T): T;
}
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
function loggingIdentity<T extends []>(arg: T): T {
console.log(arg.length); // no more error
return arg;
}
loggingIdentity(3); // Error
loggingIdentity([3]);
function extend<First, Second>(first: First, second: Second): First & Second {
const result: Partial<First & Second> = {};
for (const prop in first) {
if (first.hasOwnProperty(prop)) {
(<First>result)[prop] = first[prop];
}
}
for (const prop in second) {
if (second.hasOwnProperty(prop)) {
(<Second>result)[prop] = second[prop];
}
}
return <First & Second>result;
}
function padLeft(value: string, padding: string | number) {}
// Module
declare module "path" {
export function normalize(p: string): string;
export function join(...paths: any[]): string;
export var sep: string;
}
// Namespace
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
const lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
}
// Merging Interfaces
interface Box {
height: number;
width: number;
}
interface Box {
scale: number;
}
let box: Box = {height: 5, width: 6, scale: 10};
interface Cloner {
clone(animal: Animal): Animal;
}
interface Cloner {
clone(animal: Sheep): Sheep;
}
interface Cloner {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
}
// as
interface Cloner {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
clone(animal: Sheep): Sheep;
clone(animal: Animal): Animal;
}
// Merging Namespaces
namespace Animals {
export class Zebra { }
}
namespace Animals {
export interface Legged { numberOfLegs: number; }
export class Dog { }
}
namespace Animal {
let haveMuscles = true;
export function animalsHaveMuscles() {
return haveMuscles;
}
}
namespace Animal {
export function doAnimalsHaveMuscles() {
return haveMuscles; // Error, because haveMuscles is not accessible here
}
}
// Merging Namespaces with Classes
class Album {
label: Album.AlbumLabel;
}
namespace Album {
export class AlbumLabel { }
}
// Merging Namespaces with Fucntions
function buildLabel(name: string): string {
return buildLabel.prefix + name + buildLabel.suffix;
}
namespace buildLabel {
export let suffix = "";
export let prefix = "Hello, ";
}
console.log(buildLabel("Sam Smith"));
// Module Augmentation
declare module "./observable" {
interface Observable<T> {
map<U>(f: (x: T) => U): Observable<U>;
}
}
// Global Augmentation
declare global {
interface Array<T> {
find((x: T) => boolean): T
}
}
// based on structural subtyping(结构子类型)
interface Named {
name: string;
}
class Person {
name: string;
}
let p: Named;
// OK, because of structural typing
p = new Person();
// comparing functions
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // OK
x = y; // Error
// return type same as comparing primitive and object types
let x = () => ({name: "Alice"});
let y = () => ({name: "Alice", location: "Seattle"});
x = y; // OK
y = x; // Error, because x() lacks a location property
// function parameter bivariance
// Unsound:
(e: Event) => void
(e: MouseEvent) => void
// optional parameters and rest parameters
// Usound:
(args: any[], callback: (...args: any[]) => void)
([1, 2], (x: number, y: number) => void)
// different enum types are incompatible
enum Status { Ready, Waiting };
enum Color { Red, Blue, Green };
let status = Status.Ready;
status = Color.Green; // Error
// only compare instance properties in compatibility
// protected & private properties must originated from the same class
class Animal {
feet: number;
constructor(name: string, numFeet: number) { }
}
class Size {
feet: number;
constructor(numFeet: number) { }
}
let a: Animal;
let s: Size;
a = s; // OK
s = a; // OK
// generic types that do not have their type arguments specified, replace with any
interface NotEmpty<T> {
data: T;
}
let x: NotEmpty<number>;
let y: NotEmpty<string>;
x = y; // Error, because x and y are not compatible
let identity = function<T>(x: T): T {
// ...
}
let reverse = function<U>(y: U): U {
// ...
}
identity = reverse; // OK, because (x: any) => any matches (y: any) => any
// take place in initializing variables, setting parameter default values, determining function return types ...
let x = 3; // number
function square(x = 3) { return x * x } // (x: number) => number
// best common type
let arr = [0, 1, null] // number[]
let arr = [new Dog(), new Elephant(), new Cat()]; // Animal[] ? or (Dog|Elephant|Cat)[]
// contextual typing
window.onmousedown = function(e) {} // (e: MouseEvent) => void
function wrap<T>(v: T) {
return { value: v };
}
let o = wrap('secret'); // { value: string }
// Compiled with --strictNullChecks
let x: number;
let y: number | undefined;
let z: number | null | undefined;
x = 1; // Ok
y = 1; // Ok
z = 1; // Ok
x = undefined; // Error
y = undefined; // Ok
z = undefined; // Ok
x = null; // Error
y = null; // Error
z = null; // Ok
x = y; // Error
x = z; // Error
y = x; // Ok
y = z; // Error
z = x; // Ok
z = y; // Ok
// Compiled with --strictNullChecks
// 类型“string | null”的参数不能赋给类型“string”的参数。
// 不能将类型“null”分配给类型“string”。
export function getContentWidth(el: HTMLElement): number {
const w = el.offsetWidth;
const s = window.getComputedStyle(el);
return w - parseFloat(s.paddingLeft) - parseFloat(s.paddingRight);
}
// CGI Data
export interface Detail {
sys_time?: number; // 系统时间
cid?: number;
name?: string;
summary?: string;
cover_url?: string;
uin?: number;
// ...
}
function foo(a: number, b: string): string {
return a + b;
}
let a = foo.apply(undefined, [10]); // error: too few argumnts
let b = foo.apply(undefined, [10, 20]); // error: 2nd argument is a number
let c = foo.apply(undefined, [10, "hello", 30]); // error: too many arguments
let d = foo.apply(undefined, [10, "hello"]); // okay! returns a string
declare let f1: (x: Animal) => void;
declare let f2: (x: Dog) => void;
declare let f3: (x: Cat) => void;
f1 = f2; // Error with --strictFunctionTypes
f2 = f1; // Ok
f2 = f3; // Error
interface Comparer<T> {
compare: (a: T, b: T) => number;
}
declare let animalComparer: Comparer<Animal>;
declare let dogComparer: Comparer<Dog>;
animalComparer = dogComparer; // Error
dogComparer = animalComparer; // Ok
// ensure that each instance property of a class gets initialized in the constructor body
class C {
foo: number;
bar = "hello";
baz: boolean;
// ~~~
// Error! Property 'baz' has no initializer and is not definitely assigned in the
// constructor.
constructor() {
this.foo = 42;
}
}
// Based on never, extends
Extract<T, U> = T extends U ? T : never;
Exclude<T, U> = T extends U ? never : T;
NonNullable = T extends null ? never : T;
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends Function ? "function" :
"object";
type T0 = TypeName<string>; // "string"
type T1 = TypeName<"a">; // "string"
type T2 = TypeName<true>; // "boolean"
type T3 = TypeName<() => void>; // "function"
type T4 = TypeName<string[]>; // "object"
// infer
ReturnType<T extends (...args: any) => any> =
T extends (...args: any) => infer R
? R : any;
InstanceType<T extends new (...args: any) => any> =
T extends new (...args: any) => infer R
? R : any;
// Infers prop type from component C
type GetProps<C> = C extends ComponentType<infer P>
? P : never;
// typeof 获取隐式推断的类型
type BankSelectProps = Partial<
typeof BankSelect.defaultProps
>;
// keyof
type Readonly<T> = { readonly [P in keyof T]: T[P] };
type Partial<T> = { [P in keyof T]?: T[P] };
type Pick<T, K entends keyof T> = { [P in K]: T[P] };
type Record<K extends string | number | symbol, U> = { [P in K]: U };
// keyof (A & B) = keyof A | keyof B
// +, -
type Writable<T> = { -readonly [P in keyof T]: T[P] };
type Require<T> = { [P in keyof T]-?: T[P] };
// typeof 获取隐式推断的类型
type BankSelectProps = Partial<
typeof BankSelect.defaultProps
>;
// keyof
type Readonly<T> = { readonly [P in keyof T]: T[P] };
type Partial<T> = { [P in keyof T]?: T[P] };
type Pick<T, K entends keyof T> = { [P in K]: T[P] };
type Record<K extends string | number | symbol, U> = { [P in K]: U };
// keyof (A & B) = keyof A | keyof B
// +, -
type Writable<T> = { -readonly [P in keyof T]: T[P] };
type Require<T> = { [P in keyof T]-?: T[P] };
// as const
const PLATFORMS = ['qq', 'weixin', 'h5'] as const
By Moxhe