I read http://learnboost.github.io/stylus/docs/hashes.html and none of the examples not working for me.
For exapmle
foo = {
bar: {
baz: {
raz: 10px
}
}
}
qux = "raz"
padding
padding foo["bar"].baz[qux]
a compilation error
expected "indent", got "eos"
What did I do wrong?
You should use colon when calling hashes' values, as otherwise Stylus couldn't differentiate between selectors and hashes. So,
foo = {
bar: {
baz: {
raz: 10px
}
}
}
qux = "raz"
padding
padding: foo["bar"].baz[qux]
should work ok.
Related
I have run into a scenario where I would like the following type-enforcement:
I want the type MyArrayType to be an array of objects. The fields in the objects must be identical for each object-index in the array, but the 'catch' is that I do not know the fields beforehand. I would like them to be 'inferred'.
Here is an example of how the typing would work:
const myArrayType: MyArrayType = [{ foo: 2 }, { foo: 1 }] // this will work
const myArrayType: MyArrayType = [{ foo: 2 }, { bar: 1 }] // this will fail
const myArrayType: MyArrayType = [{ bar: 2 }, { bar: 1 }] // this will work
const myArrayType: MyArrayType = [{ anyFieldName: 2 }, { anyFieldName: 1 }] // this will work
const myArrayType: MyArrayType = [{ bar: 2, foo: 1 }, { bar: 1, foo: 2 }] // this will work
const myArrayType: MyArrayType = [{ bar: 2, foo: 1 }, { bar: 1 }] // this will fail
The following should also fail:
const myArrayType: MyArrayType = [1,2,3].map((number) => {
if (number === 2) {
return {
foo: 1
bar: 2
}
}
return {
bar: 2
}
})
And this should work:
const myArrayType: MyArrayType = [1,2,3].map((number) => {
return {
foo: 1
bar: 2
}
})
Is this possible to achieve with typescript as of 2021?
Additional constraints:
Compile-time checking. Runtime checking is useful but not required.
Wrapper functions and helpers are OK.
Ideally, the function applies excess property checking as well, throwing errors if unexpected extra properties are present on values beyond the first.
You can express the first part of this with this generic function:
function check<T1, T2 extends T1>(t1: T1, ...tRest: T2[]): T1[] {
return [t1, ...tRest];
}
// Accepts an entire array, courtesy Jeff Mercado (thank you!).
function checkArray<T1, T2 extends T1>(arr: [T1, ...T2[]]): T1[] {
return arr;
}
Playground Link
As far as the map is concerned, TypeScript correctly infers the union return type of the callback function. Your best bet there is probably to "prohibit" a union by forcing the type to never, as you can do with existing tools like this example by Titian Cernicova-Dragomir:
type UnionToIntersection<U> =
(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void)
? I : never
type NoUnion<Key> =
// If this is a simple type UnionToIntersection<Key> will be the same type,
// otherwise it will an intersection of all types in the union and probably
// will not extend `Key`
[Key] extends [UnionToIntersection<Key>] ? Key : never;
I have a function that receives an intersection of two types:
interface A {
propA: string;
}
interface B {
propB: number;
propC: boolean;
}
function C(props: A & B) {
}
Now, inside the function body I would like to get objects containing only the subset of properties from each of the interfaces; so I was wondering if Typescript had any utility to achieve that:
function C(props: A & B) {
const a = fancyPicker<A>(props);
const b = fancyPicker<B>(props);
console.log(a);
// prints "{ propA: "some string" }"
console.log(b);
// prints "{ propB: 42, propC: false }"
}
You're after a function that iterates over a known set of property names - this can't be done in pure TypeScript because TypeScript uses type-erasure, so the runtime script has no-knowledge of what the set of property names is.
But using a TypeScript compile-time extension known as a Custom Transformer, specifically ts-transformer-keys the TypeScript compiler will emit property-name lists that can be used.
Here's something that works, but isn't perfect because it doesn't use the types of each property - it only matches names:
import { keys } from 'ts-transformer-keys'; // <-- This module is where the magic happens.
type IndexableObject = {
[key: string]: any
};
/*
* The `IndexableObject` above is a hack. Note it allows `any` property type. Ideally it'd be something like this instead, but using `keyof` in a type indexer is not yet supported: https://github.com/microsoft/TypeScript/pull/26797
*
type IndexableObject<TOther> = {
[key: TKey extends keyof TOther]: PropertyType<TOther,TKey>
};
*/
function fancyPicker<TSubset extends object>(superset: IndexableObject): Partial<TSubset> {
const subsetPropertyNames = keys<TSubset>();
const ret: Partial<TSubset> = {
};
for (const subsetPropertyName of subsetPropertyNames) {
const propName: string = subsetPropertyName as string; // <-- This is also a hack because property keys/names are actually `string | number | symbol` - but this function assumes they're all named properties.
if (propName in superset) {
const value = superset[propName];
ret[subsetPropertyName] = value;
}
}
return ret;
}
Usage (using your example):
interface A {
propA: string;
}
interface B {
propB: number;
propC: boolean;
}
type ABIntersection = A & B;
type ABUnion = A | B;
function C(props: ABIntersection) {
const a = fancyPicker<A>(props);
const b = fancyPicker<B>(props);
console.log(a);
// prints "{ propA: "some string" }"
console.log(b);
// prints "{ propB: 42, propC: false }"
}
const testValue = { propA: "some string", propB: 42, propC: false, propD: "never see this" };
C(testValue);
I have the following function, it iterates over an array of Foo, and creates a new array using the items in the original array, but with different colors:
extension Collection where Iterator.Element == Foo {
public func color(colors: [NSColor]) -> [Foo] {
var result: [Foo] = []
for item in self {
for color in colors {
let newItem = Foo(name: item.name, color: color)
result.append(newItem)
}
}
return result
}
}
This works, but if I use it on a subclass Bar of Foo, it still returns [Foo], not [Bar].
I could do something like this:
let colors: [NSColor] = // set colors
let colorArray = array.color(colors).map{ Bar($0.name, $.color)
But then I need to remember to do that every time I call it for a subclass of Foo.
So how can I adjust the function to make it work on subclasses of Foo as well?
EDIT
Based on the comment below, I tried a generic function:
public func color<T>(colors: [NSColor]) -> [T] {
var result: [T] = []
for item in self {
for color in colors {
let newItem = T(name: item.name, color: color)
result.append(newItem)
}
}
return result
}
That gives me the error:
Non-nominal type 'T' does not support explicit initialization
So I searched for that, and found I need to use init:
let newItem = T.init(name: item.name, color: color)
Now I get this error:
Type 'T' has no member 'init'
Foo and Bar have an init, but that doesn't help here. Am I on the right track?
EDIT 2:
Martin's answer below made me realize an error in my code: where Iterator.Element == Foo should have been where Iterator.Element: Foo
You can replace the “same-type constraint” by a “subclass constraint” and use the collections Element type instead of Foo in the implementation:
extension Collection where Element: Foo {
func color(colors: [NSColor]) -> [Element] {
var result: [Element] = []
for item in self {
for color in colors {
let newItem = Element(name: item.name, color: color)
result.append(newItem)
}
}
return result
}
}
Note that this requires Foo (and its subclasses) to have a “required init” method:
required init(name: String, color: NSColor)
Another option is to define a protocol
protocol P {
var name: String { get }
init(name: String, color: NSColor)
}
to which Foo (and its subclasses) conform, and use the constraint
extension Collection where Element: P
for the extension method.
In any case, a more concise implementation would be
extension Collection where Element: Foo {
func color(colors: [NSColor]) -> [Element] {
return self.flatMap { item in
colors.map { Element(name: item.name, color: $0) }
}
}
}
Re your edit: As a free generic method (instead of an extension method) it would be something like
func color<T: P>(items: [T], with colors: [NSColor]) -> [T] {
return items.flatMap { item in
colors.map { T(name: item.name, color: $0) }
}
}
using the protocol defined above. The compiler needs to know that an instance of T can be created from an item name and a color.
I'm trying to dynamically populate my pieChartColors array, so that in the end my array will look something like this:
public pieChartColors:Array<Color> = [
{
backgroundColor: '#141C48'
},
{
backgroundColor: '#FF0000'
},
{
backgroundColor: '#EFEFEF'
},
...
]
I'm starting with a blank array and have tried a few different ways to get the values added, but none of them are working (my color values are stored without the hash symbol):
public pieChartColors: Array<Color> = [];
public buildPieChart() {
...
for (let pie of pieData) {
// none of these work
this.pieChartColors.push(backgroundColor: '#'+pie.backgroundColor);
this.pieChartColors.backgroundColor.push('#'+pie.backgroundColor);
this.pieChartColors['backgroundColor'].push('#'+pie.backgroundColor);
}
...
}
BTW, pieData is an object I'm looping through from the database with the backgroundColor values. If I console.log the pie.backgroundColor, the value is available.
Just do it like in JavaScript:
this.pieChartColors.push({backgroundColor: '#'+pie.backgroundColor});
Just learning swift and wanted to rotate through an array of colors like so:
class ColorSwitcher
{
let colors:String[] = ["red", "blue", "green"]
var currIndex:Int?
var selectedColor:String{
return self.colors[currIndex!]
}
init(){
currIndex = 0
}
func changeColor()
{
currIndex++ //this doesn't work
}
}
When I try to call the function like so:
var switcher:ColorSwitcher = ColorSwitcher()
switcher.selectedColor // returns red
switcher.changeColor()
switcher.selectedColor // still returns red
The problem is with the changeColor function. The error I get is:
Could not find an overload for '++' that accepts the supplied arguments
What am I doing wrong?
The problem is that currIndex is an optional. I'd suggest refactoring like so:
class ColorSwitcher {
let colors:String[] = ["red", "blue", "green"]
var currIndex:Int = 0
var selectedColor:String {
return self.colors[currIndex]
}
func changeColor() {
currIndex++
}
}
if you want to keep it an optional you'll need to do this:
currIndex = currIndex! + 1
but of course that isn't safe, so you should probably do:
if let i = currIndex {
currIndex = i + 1
}
else {
currIndex = 1
}
Also, keep in mind that you don't need to use an optional if you're going to set the value in your init(). The following is fine:
class ColorSwitcher {
let colors:String[] = ["red", "blue", "green"]
var currIndex:Int
init(startIndex: Int) {
currIndex = startIndex
}
var selectedColor:String {
return self.colors[currIndex]
}
func changeColor() {
currIndex++
}
}
you can overload the missing ++ operator for optional Int, like e.g. this:
#assignment #postfix func ++(inout x: Int?) -> Int? {
if x != nil {
x = x! + 1
return x
} else {
return nil
}
}
or you can change your class, like e.g. this:
class ColorSwitcher {
let colors:String[] = ["red", "blue", "green"]
var currIndex: Int = 0
var selectedColor: String {
return self.colors[currIndex]
}
func changeColor() {
currIndex++
}
}
NOTE: that does not hold any improvement in your class's internal behaviour. it will do the same for you as did in your OP.